You can speed it up a lot by loading the data more directly without using binary:
{-# INLINE unconsW8 #-}
unconsW8 :: BSL.ByteString -> Maybe (Word8, BSL.ByteString)
unconsW8 = BSL.uncons
{-# INLINE unconsW16 #-}
unconsW16 :: BSL.ByteString -> Maybe (Word16, BSL.ByteString)
unconsW16 bs = do
(x, bs') <- unconsW8 bs
(y, bs'') <- unconsW8 bs'
pure (fromIntegral x .|. (fromIntegral y `shiftL` 8), bs'')
{-# INLINE unconsW32 #-}
unconsW32 :: BSL.ByteString -> Maybe (Word32, BSL.ByteString)
unconsW32 bs = do
(x, bs') <- unconsW16 bs
(y, bs'') <- unconsW16 bs'
pure (fromIntegral x .|. (fromIntegral y `shiftL` 16), bs'')
{-# INLINE unconsFloat #-}
unconsFloat :: BSL.ByteString -> Maybe (Float, BSL.ByteString)
unconsFloat bs = case unconsW32 bs of
Just (x, xs) -> Just (castWord32ToFloat x, xs)
Nothing -> Nothing
main = do
pixelsRaw <- BSL.readFile "matrix.bin"
let pixels = SV.unfoldrExactN (5000*5000) (\s -> fromMaybe (0, s) (unconsFloat s)) pixelsRaw
BSL.writeFile "/tmp/twotime-bsl.svg" (pageSvg pixels)
That also removes all GC overhead:
before:
% cabal run exes -- +RTS -s
4,371,906,064 bytes allocated in the heap
5,643,847,856 bytes copied during GC
1,000,154,664 bytes maximum residency (12 sample(s))
170,273,240 bytes maximum slop
2360 MiB total memory in use (0 MiB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 882 colls, 0 par 0.884s 0.889s 0.0010s 0.0026s
Gen 1 12 colls, 0 par 0.716s 0.891s 0.0743s 0.3514s
INIT time 0.002s ( 0.002s elapsed)
MUT time 1.266s ( 1.120s elapsed)
GC time 1.600s ( 1.780s elapsed)
EXIT time 0.032s ( 0.010s elapsed)
Total time 2.899s ( 2.911s elapsed)
%GC time 0.0% (0.0% elapsed)
Alloc rate 3,453,990,751 bytes per MUT second
Productivity 43.7% of total user, 38.5% of total elapsed
after:
% cabal -O2 run exes -- +RTS -s
3,001,347,728 bytes allocated in the heap
688,096 bytes copied during GC
100,053,152 bytes maximum residency (4 sample(s))
667,488 bytes maximum slop
237 MiB total memory in use (1 MiB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 602 colls, 0 par 0.001s 0.002s 0.0000s 0.0000s
Gen 1 4 colls, 0 par 0.001s 0.004s 0.0009s 0.0023s
INIT time 0.002s ( 0.002s elapsed)
MUT time 0.835s ( 0.832s elapsed)
GC time 0.002s ( 0.005s elapsed)
EXIT time 0.001s ( 0.006s elapsed)
Total time 0.839s ( 0.845s elapsed)
%GC time 0.0% (0.0% elapsed)
Alloc rate 3,595,707,368 bytes per MUT second
Productivity 99.5% of total user, 98.5% of total elapsed
Edit: SV.unfoldrExactN is much better than SV.replicateM