For my vector graphics library in Haskell I must carry around a rather big state: line stroke parameters, colors, clip path etc. I know two ways of doing this. Quoting a comment from Haskell-cafe: "I would suggest you either use a reader monad with mutable state, or a state monad with immutable state".
Here is my problem: updating a big immutable state is a performance kill. Using lots of STRefs is like writing C in Haskell: it's verbose and ugly.
Here is the immutable state:
data GfxState = GfxState {
lineWidth :: Double,
lineCap :: Int,
color :: Color,
clip :: Path,
...
}
setLineWidth :: Double -> State GfxState ()
setLineWidth x = modify (\state -> state { lineWidth = x })
As far as I know, "state { lineWidth = x }" creates a new GfxState and lets the old one be garbage collected. This kills performance when the state is big and updated often.
Here is the mutable state:
data GfxState s = GfxState {
lineWidth :: STRef s Double,
lineCap :: STRef s Int,
color :: STRef s Color,
clip :: STRef s Path,
...
many more STRefs
}
setLineWidth :: GfxState s -> Double -> ST s ()
setLineWidth state x = writeSTRef (lineWidth state) x
Now I get (GfxState s) and (ST s) and (STRef s) all over the place, which is verbose, confusing, and beats the spirit of writing short and expressive code. I could use C + FFI to read and update the big state, but since I encounter this pattern quite often, I'm hoping there's a better way.
As an aside, you should certainly be improving your data type representation via unboxing, if you are concerned about performance:
data GfxState = GfxState {
lineWidth :: {-# UNPACK #-}!Double,
lineCap :: {-# UNPACK #-}!Int,
color :: {-# UNPACK #-}!Color,
clip :: Path,
...
}
By unpacking the constructors, you improve the density of your data, going from a heap structure like this:
to the denser, stricter:
Now all the atomic types are laid out in consecutive memory slots. Updating this type will be much faster! BTW, 461.. is the Word representation of the pi field, a bug in my viewer library
You'll also reduce the chance of space leaks.
The cost of passing such a structure around will be very cheap, as the components will be stored in registers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With