Help with optimization/profiling

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

1 Like