I am comparing Haskell array libraries ( arrayand packages vector) to find the best way to store big data for my use case. I use criterionas a comparison tool.
In short: my code simply selects a vector and proceeds to fill it with simple structures (1M, 10M and 100M elements, respectively). When I compare the Haskell test time with a simple reference implementation that I wrote in C, Haskell is several times faster, and I find this suspicious: C code is a simple loop filling structures in an array.
Question: Is it possible for the Haskell library to vectorbeat C in terms of performance? Or does this mean that my tests are wrong / something is not really being evaluated / is there some gotcha?
Another question is how to make sure that the Haskell vectors are actually evaluated?
Longer explanation: The task is to fill the vector with more structures. They have instances Storable, and the vector used is Data.Vector.Storable.
The data type is as follows:
data Foo = Foo Int Int deriving (Show, Eq, Generic, NFData)
And the instances Storablelook like this:
chunkSize :: Int
chunkSize = sizeOf (undefined :: Int)
{-# INLINE chunkSize #-}
instance Storable Foo where
sizeOf _ = 2 * chunkSize ; {-# INLINE sizeOf #-}
alignment _ = chunkSize ; {-# INLINE alignment #-}
peek ptr = Foo
<$> peekByteOff ptr 0
<*> peekByteOff ptr chunkSize
{-# INLINE peek #-}
poke ptr (Foo a b) = do
pokeByteOff ptr 0 a
pokeByteOff ptr chunkSize b
{-# INLINE poke #-}
Serialization itself is working fine. Then the vector is highlighted:
mkFooVec :: Int -> IO (Vector Foo)
mkFooVec !i = unsafeFreeze =<< new (i + 1)
And is filled with structures:
populateFooVec :: Int -> Vector Foo -> IO (Vector Foo)
populateFooVec !i !v = do
v' <- unsafeThaw v
let go 0 = return ()
go j = unsafeWrite v' j (Foo j $ j + 1) >> go (j - 1)
go i
unsafeFreeze v'
A benchmark is a standard criterion:
defaultMain [
bgroup "Storable vector (mutable)"
$ (\(i :: Int) -> env (mkFooVec (10 ^ i))
$ \v -> bench ("10e" <> show i)
$ nfIO (populateFooVec (10 ^ i) v)) <$> [6..8]
]
gist contains other tests, trying to force the evaluation in different ways.
C-, , (gist). :
Foo *allocFoos(long n) {
return (Foo *) malloc(n * sizeof(Foo));
}
void createFoos(Foo *v, long n) {
for (long i = 0; i < n; ++i) {
v[i].name = i;
v[i].id = i + 1;
}
}
, : gcc -O2 -o bench benchmark.c && ./bench
, , C 50 , Criterion - 800 (!). : , ? , (, Haskell, -). ? - vector C ( GCC , btw)?
, ;)