[Haskell-cafe] Memory usage when passing arrays in state

Daniel Fischer daniel.is.fischer at web.de
Tue Mar 3 20:38:56 EST 2009


Am Mittwoch, 4. März 2009 01:44 schrieb Tobias Olausson:
> Hello all.
> I am currently implementing an emulation of a CPU, in which the CPU's
> RAM is part of the internal state
> that is passed around in the program using a state monad. However, the
> program performs
> unexpectingly bad, and some profiling information makes us believe
> that the problem is the high
> memory usage of the program.
>
> The program below is similar to our main program used when testing a
> sorting algorithm in this CPU:
>
> module Main where
>
> import Control.Monad.State.Lazy

Not good, use Control.Monad.State.Strict

> import Data.Word
> import Data.Array.Diff
> import Control.Concurrent (threadDelay)
>
> data LoopState = LoopState
>     { intVal :: Integer
>     , diff   :: DiffUArray Word8 Word8

Diff(U)Arrays tend to be slow, use them with care.

>     }
>
> initState :: LoopState
> initState = LoopState 0 (array (0x00,0xFF) [(idx,0)|idx<-[0x00..0xFF]])
>
> main :: IO ()
> main = do
>     execStateT looper initState >>= putStrLn . show . intVal
>
> looper :: StateT LoopState IO ()
> looper = do
>     st <- get
>     let res = intVal st + 1
>         idx = fromIntegral res
>     put $ st { intVal = res, diff = (diff st) // [(idx,idx)] }
>     if res == 13000000
>         then return ()
>         else looper

You're being too lazy, building a huge thunk that only gets evaluated at the 
end of the loop. You have to force evaluation earlier.

>
> Of course our program does more than updating a counter ;-)
> Compiling and running this program yields the following result:
>
> [~]:[olaussot] >> ghc --make -O2 -o array ArrayPlay.hs
> [~]:[olaussot] >> ./array +RTS -sstderr
> ./array +RTS -sstderr
> 13000000
>      313,219,740 bytes allocated in the heap
>    1,009,986,984 bytes copied during GC
>      200,014,828 bytes maximum residency (8 sample(s))
>        4,946,648 bytes maximum slop
>              393 MB total memory in use (3 MB lost due to fragmentation)
>
>   Generation 0:   590 collections,     0 parallel,  3.06s,  3.09s elapsed
>   Generation 1:     8 collections,     0 parallel,  3.56s,  4.21s elapsed
>
>   INIT  time    0.00s  (  0.00s elapsed)
>   MUT   time    0.27s  (  0.27s elapsed)
>   GC    time    6.62s  (  7.30s elapsed)
>   EXIT  time    0.00s  (  0.00s elapsed)
>   Total time    6.89s  (  7.57s elapsed)
>
>   %GC time      96.1%  (96.4% elapsed)
>
>   Alloc rate    1,155,958,754 bytes per MUT second
>
>   Productivity   3.9% of total user, 3.6% of total elapsed
>
> Why does the program spend 96.1% of its total running time collecting
> garbage? Any tips to make this program perform better are appreciated.
> Please do tell if anything is unclear.

Nothing gets evaluated until the end, so nothing can be discarded earlier.

----------------------------------------------------------------------
{-# LANGUAGE BangPatterns #-}
module Main where

import Control.Monad.State.Strict
import Data.Word
import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.MArray

update :: UArray Word8 Word8 -> Word8 -> Word8 -> UArray Word8 Word8
update arr i v = runSTUArray $ do
    sar <- unsafeThaw arr
    writeArray sar i v
    return sar

data LoopState = LoopState
    { intVal :: !Integer
    , diff   :: !(UArray Word8 Word8)
    }

initState :: LoopState
initState = LoopState 0 (array (0x00,0xFF) [(idx,0)|idx<-[0x00 .. 0xFF]])

main :: IO ()
main = do
    execStateT looper initState >>= putStrLn . show . intVal

looper :: StateT LoopState IO ()
looper = do
    LoopState i df <- get
    let res = i + 1
        idx = fromIntegral res
        !ndf = update df idx idx
    put (LoopState res ndf)
    if res == 13000000
        then return ()
        else looper
----------------------------------------------------------------------

Is much better behaved. I didn't investigate if every strictness annotation is 
necessary.

>
> --
> Tobias Olausson
> tobsan at gmail.com

Cheers,
Daniel



More information about the Haskell-Cafe mailing list