|
confusing space use and space profiles.: msg#00091lang.haskell.glasgow.bugs
I'm totally confused by the space profiling. Let me explain. Here's a simple log file processing program. It prints the 10 most frequently occurring lines in a file. import Data.List import qualified Data.Map as Map import qualified Data.ByteString.Lazy.Char8 as BS main = BS.interact $ BS.unlines . map fst . take 10 . sortBy (comparingReversed snd) . Map.toList . foldl' accumulate Map.empty . BS.lines where accumulate map line = Map.insertWith (\_ n -> n+1) line 1 map comparingReversed f a b = f b `compare` f a Here I'm using ByteString.Lazy because it's faster than ByteString. That's probably because it allows us to consume the input incrementally. However using ByteString.Lazy uses much more space than when using strict ByteString. For an 11Mb log file: * the strict version needs at least 18Mb heap (and takes 8.5s) * the lazy version needs at least 55Mb heap (and takes 1.9s) This is surprising as I would have expected nearly the same space use. The lazy version will use more cons cells and PS constructors but this should still be fairly negligible as it reads in 32k chunks. So I tried memory profiling with the lazy version. Using -hc or -hd indicates that we are only using about 27Mb which is just what I would have expected, furthermore the breakdown by cost centre and closure is about what I would have expected. In particular the ARR_WORDS closure type which is for the #MutableByteArray is exactly the size of the file that was read. The PS and (:) closures take a fair amount which is because there's one per line in the file, and the lines are not that long on average. The Bin constructors of the finite map take much of the rest. So if the heap profile says that we're using at most 27Mb then why does the program run out of heap when allowed any less than 55Mb ? This seems suspiciously close to a factor of 2. At first I suspected that it might be that we were inefficiently allocating chunks, but we're reading from stdin in chunks of size: defaultChunkSize :: Int defaultChunkSize = 32 * k - overhead where k = 1024 overhead = 2 * sizeOf (undefined :: Int) which should be reasonable I think and not give us lots of half used memory blocks. All other strings we create are substrings so share space and only take space for the PS and (:) constructors. Ok, here's an odd thing. When profiling the strict version, the -hd closure description doesn't show the ARR_WORDS closure type at all. But there should be one great big 11Mb MutableByteArray#. It shows the other closure types taking about 13Mb. Did I guess wrong about what ARR_WORDS means? I was doing all this with ghc-6.5.20060724 on amd64. I got essentially the same results with ghc-6.4.2. The only profiling option that shows the full heap size was biographical profiling. The hp2ps plot from using -hb peaked at 55Mb. In ghc-6.4.2 it was all marked as drag. I'm not exactly sure what that means. With ghc-6.5.20060724 I got a bit of all the categories: drag, inherent_in_use, void, lag and use. Again it peaks at 55Mb with drag the dominant one. With the strict ByteString it peaks at about 16M. Duncan
|
|
| <Prev in Thread] | Current Thread | [Next in Thread> |
|---|---|---|
| Previous by Date: | Re: [GHC] #861: -prof without profiling libraries fails without explanation, GHC |
|---|---|
| Next by Date: | [GHC] #863: minor oddity in kind error message, GHC |
| Previous by Thread: | [GHC] #862: Document changes to packages, GHC |
| Next by Thread: | Re: confusing space use and space profiles., Simon Marlow |
| Indexes: | [Date] [Thread] [Top] [All Lists] |
| News | FAQ | advertise |