修复一个特别模糊的Haskell空间泄漏

Eri*_*ton 8 haskell memory-leaks space

因此,在用一些Haskell压缩了最后一点性能后,我正在使用将推文数据分解为n-gram,我遇到了空间泄漏问题.当我进行分析时,GC使用大约60-70%的过程,并且有大量内存部分专用于拖动.希望一些Haskell大师能够在我出错的时候提出建议.

{-# LANGUAGE OverloadedStrings, BangPatterns #-}
import Data.Maybe
import qualified Data.ByteString.Char8 as B
import qualified Data.HashMap.Strict as H
import Text.Regex.Posix
import Data.List
import qualified Data.Char as C

isClassChar a = C.isAlphaNum a || a == ' ' || a == '\'' ||
                a == '-' || a == '#' || a == '@' || a == '%'

cullWord :: B.ByteString -> B.ByteString
cullWord w = B.map C.toLower $ B.filter isClassChar w

procTextN :: Int -> B.ByteString -> [([B.ByteString],Int)]
procTextN n t = H.toList $ foldl' ngram H.empty lines
  where !lines        = B.lines $ cullWord t
        ngram tr line = snd $ foldl' breakdown (base,tr) (B.split ' ' line)
        base          = replicate (n-1) ""

breakdown :: ([B.ByteString], H.HashMap [B.ByteString] Int) -> B.ByteString -> ([B.ByteString],H.HashMap [B.ByteString] Int)
breakdown (st@(s:ss),tree) word =
  newStack `seq` expandedWord `seq` (newStack,expandedWord)
      where newStack     = ss ++ [word]
            expandedWord = updateWord (st ++ [word]) tree

updateWord :: [B.ByteString] -> H.HashMap [B.ByteString] Int -> H.HashMap [B.ByteString] Int
updateWord w h = H.insertWith (+) w 1 h

main = do
  test2 <- B.readFile "canewobble"
  print $ filter (\(a,b) -> b > 100) $ 
     sortBy (\(a,b) (c,d) -> compare d b) $ procTextN 3 test2
Run Code Online (Sandbox Code Playgroud)

Mik*_*kov 7

一个小的优化是HashMap.filter在排序之前过滤数据(使用).这帮助我减少了最后执行时间2秒.我做的另一件事是使用序列(Data.Sequence)而不是列表(没有明显的区别:-().我的版本可以在这里找到.

查看堆配置文件,我认为您的程序中没有空间泄漏: 空间概况

你只是在内存中构建一个相当大的哈希表(377141个键值对),然后在一些处理之后将其丢弃.根据约翰的帖子,这个大小的哈希表需要约.5*N + 4*(N-1)个字= 3394265*4个字节〜= 13个MiB,这与堆配置文件显示的内容一致.剩余空间由键和值获取.在我的机器上,在GC中花费的时间大约是40%,这听起来并不合理,因为你不断更新哈希表和临时"堆栈",而没有做任何计算繁重的数据.由于您需要哈希表的唯一操作是insertWith,或许最好使用可变数据结构

更新:我使用可变哈希表重写了您的程序.有趣的是,速度差异并不大,但内存使用情况略好一些:

在此输入图像描述

如您所见,为哈希表分配的块大小在整个执行过程中保持不变.