GHC的垃圾收集RTS选项
我有一个Haskell程序处理一个文本文件并构建一个Map
(有几百万个元素)。 整个事情可以运行2-3分钟。 我发现调整-H和-A选项在运行时间上有很大的不同。
有关于RTS的这个功能的文档,但是对我来说这是一篇难以理解的文章,因为我不知道GC理论中的算法和术语。 我正在寻找一个技术性较差的解释,最好是针对Haskell / GHC。 有没有关于为这些选项选择合理值的参考?
编辑:这是代码,它为给定的单词列表建立一个句号。
buildTrie :: [B.ByteString] -> MyDFA
buildTrie l = fst3 $ foldl' step (emptyDFA, B.empty, 1) $ sort $ map B.reverse l where
step :: (MyDFA , B.ByteString, Int) -> B.ByteString -> (MyDFA , B.ByteString, Int)
step (dfa, lastWord, newIndex) newWord = (insertNewStates, newWord, newIndex + B.length newSuffix) where
(pref, lastSuffix, newSuffix) = splitPrefix lastWord newWord
branchPoint = transStar dfa pref
--new state labels for the newSuffix path
newStates = [newIndex .. newIndex + B.length newSuffix - 1]
--insert newStates
insertNewStates = (foldl' (flip insertTransition) dfa $ zip3 (branchPoint:init newStates) (B.unpack newSuffix) newStates)
一般来说,垃圾收集是一种空间/时间的折衷。 为GC提供更多的空间,并且需要更少的时间。 游戏中还有(很多)其他因素,特别是缓存,但空间/时间折衷是最重要的因素。
权衡是这样工作的:程序分配内存直到达到某个限制(由GC的自动调整参数决定,或通过RTS选项明确指定)。 达到限制时,GC跟踪程序当前正在使用的所有数据,并回收不再需要的数据所使用的所有内存。 您可以延迟此过程的时间越长,在此期间越多的数据将变得无法访问(“死亡”),因此GC可以避免跟踪该数据。 推迟GC的唯一方法是让更多的内存可用于分配; 因此更多的内存等于更少的GC,等于更低的GC开销。 粗略地说,GHC的-H选项可以让您设置GC使用的内存量的下限,因此可以降低GC开销。
GHC使用分代GC,这是对基本方案的优化,其中堆被分成两代或更多代。 对象被分配到“年轻”一代,并且足够长的那些被提升到“旧”一代(在2代设置中)。 年轻一代的收集比老一代要频繁得多,这个想法是“大多数物体都会年轻化”,所以年轻一代的收藏品很便宜(它们没有追踪太多数据),但它们回收了大量的记忆。 粗略地说,-A选项设置年轻一代的规模 - 也就是说,在收集年轻一代之前将分配的内存量。
-A的缺省值为512k:将年轻一代保持为小于L2缓存是一个好主意,并且如果超过L2缓存大小,性能通常会下降。 但是在相反的方向上工作是GC空间/时间的折中:通过减少GC的工作量,使用非常大的年轻一代的规模可能会超过缓存的好处。 这并不总是会发生,这取决于应用程序的动态性,这使GC很难自动调整自己。 -H选项也增加了年轻一代的大小,因此也可能对高速缓存使用产生不利影响。
底线是:玩弄选项,看看有什么作用。 如果您有足够的内存可用,则可以通过使用-A或-H来获得性能提升,但不一定。
对于较小的数据集,可能会重现该问题,在这里可以更容易地看到正在发生的事情。 特别是,我建议熟悉一下分析:
然后,检查你看到的内存配置文件是否符合你的期望(你不需要知道所有的分析选项来获得有用的图表)。 严格foldl'
与非严格元组的组合作为累加器将是我首先要看的:如果元组的组成部分不是强制的,那么递归会构建未经评估的表达式。
顺便说一句,你可以通过尝试对真正微小的数据集手工评估你的代码来建立一个关于这些事情的良好直觉。 几次迭代就足以查看表达式是否得到评估或以适合您的应用程序的方式保持未评估。
链接地址: http://www.djcxy.com/p/43225.html上一篇: GHC's RTS options for garbage collection
下一篇: exhaustive pattern matches only because I left off `otherwise =`?