Storable and constant memory

Roman Leshchinskiy rl at cse.unsw.edu.au
Sat Apr 17 05:29:14 EDT 2010


Here is a problem that affects package vector, sometimes resulting in much slower code with Data.Vector.Storable (based on ForeignPtr and Storable) compared to Data.Vector.Unboxed (based on ByteArrays). Suppose we have a recursive function:

foo :: Vector Int -> Maybe Int -> ...
foo v x = ... foo v (Just (v ! i)) ...

If v comes from Data.Vector.Unboxed, this eventually becomes:

foo v x = ... foo v (Just (I# (indexIntArray# arr# i#))) ...

Now SpecConstr kicks in, specialises foo for this particular call pattern and we get a nice unboxed loop:

foo' :: Vector Int -> Int# -> ...
foo' v n# = let x = Just (I# n#) in ... foo' v (indexIntArray# arr# i#) ...

Alas, this doesn't work for Data.Vector.Storable. Here, we have to use peek which gives us this:

foo v x = ... foo v (Just (case readIntOffAddr# p# i# realWorld# of { (# s#, n# #) ->
                           case touch# fp s# of { _ -> I# n# }})) ...

SpecConstr eliminates the Just but can't unbox the Int. With enough fiddling, it would be possible to implement collective operations such that they only touch# once at the end rather than after each element access. This significantly complicates stream fusion, however, and doesn't help with SpecConstr anyway since the stateful read still gets in the way.

So the question is: would it perhaps be a good idea for Storable to include support for marshalling from constant memory? Also, can we do something about touch#? Perhaps it could have type (a -> b -> b) and then we could make SpecConstr look through it? 

Roman




More information about the Libraries mailing list