Seb*_*ser 48 performance garbage-collection haskell memory-management
目前我正在尝试使用Snap编写的一个小Haskell Web服务器来加载并向客户端提供大量数据.而且我非常非常难以控制服务器进程.在随机的瞬间,该过程使用大量的CPU几秒到几分钟,并且对客户端请求没有反应.有时内存使用量会在几秒钟内达到峰值(有时会下降)数百兆字节.
希望有人对使用大量内存的长时间运行的Haskell进程有更多的经验,并且可以给我一些指导以使事情更稳定.我已经调试了几天了,我开始有点绝望了.
我的设置概述:
在服务器启动时,我将大约5千兆字节的数据读入内存中的大型(嵌套)Data.Map相似结构.嵌套映射是值严格的,并且映射中的所有值都是数据类型,其所有字段也都是严格的.我花了很多时间确保没有留下未评估的thunk.导入(取决于我的系统负载)大约需要5-30分钟.奇怪的是连续运行的波动比我预期的要大,但这是一个不同的问题.
大数据结构存在于由"Snap"服务器生成的所有客户端线程共享的"TVar"中.客户端可以使用小型查询语言请求数据的任意部分.数据请求的数量通常很小(高达300kb左右),只接触数据结构的一小部分.所有只读请求都使用'readTVarIO'完成,因此它们不需要任何STM事务.
服务器使用以下标志启动:+ RTS -N -I0 -qg -qb.这将以多线程模式启动服务器,禁用空闲时间和并行GC.这似乎加快了这个过程.
服务器大多运行没有任何问题.但是,偶尔客户端请求会超时并且CPU达到100%(甚至超过100%),并且会持续这么长时间.同时服务器不再响应请求.
我可以想到几个可能导致CPU使用的原因:
请求只需要花费很多时间,因为还有很多工作要做.这有点不太可能,因为有时它会发生在先前运行中证明非常快的请求(快速我的意思是20-80ms左右).
在处理数据并将数据发送到客户端之前,仍然需要计算一些未评估的thunk.这也不太可能,原因与前一点相同.
以某种方式垃圾收集开始并开始扫描我的整个5GB堆.我可以想象这可能会花费很多时间.
问题是我不知道如何弄清楚究竟发生了什么,以及如何处理这个问题.因为导入过程花了这么长时间的分析结果,所以没有向我展示任何有用的东西.似乎没有办法从代码中有条件地打开和关闭探查器.
我个人怀疑GC是问题所在.我正在使用GHC7,它似乎有很多选项可以调整GC的工作原理.
在使用通常非常稳定的数据的大堆时,您建议使用哪些GC设置?
Edw*_*ang 29
大量的内存使用和偶尔的CPU峰值几乎肯定是GC的开头.你可以通过使用RTS选项来确定是否确实如此-B,这会导致GHC在有主要集合时发出蜂鸣声,-t这将告诉你事后的统计数据(特别是,看看GC时间是否真的很长),或者-Dg打开GC调用的调试信息(尽管你需要编译-debug).
您可以采取以下措施来缓解此问题:
在最初导入数据时,GHC正在浪费大量时间来增加堆.您可以通过指定一个大的来告诉它一次性获取所需的所有内存-H.
具有稳定数据的大型堆将被提升为旧一代.如果你增加了代数-G,你可能能够将稳定的数据放在最古老的,非常少的GC代中,而你有更传统的年轻和旧的堆.
根据应用程序其余部分的内存使用情况,您可以使用它-F来调整GHC在再次收集之前让旧代增长多少.您可以调整此参数以使此垃圾收集.
如果没有写入,并且您有一个定义良好的接口,那么可能值得使GHC(使用C FFI)不管理此内存,这样就不会有超级GC.
这些都是猜测,所以请测试您的特定应用程序.