目前,我正在试验一个用Snap编写的Haskell小型Web服务器,该服务器会加载并向客户端提供大量数据。 而且,我很难控制服务器进程。 在随机时刻,该进程占用大量CPU数秒至数分钟,并且对客户端请求无响应。 有时,内存使用率会在几秒钟内激增(有时会下降)数百兆字节。
希望有人对长时间运行的Haskell进程有更多的经验,这些进程会占用大量内存,并且可以为我提供一些使事情变得更稳定的指示。 我已经调试了好几天了,在这里我开始感到绝望了。
我的设置的一些概述:
在服务器启动时,我将大约5 GB的数据读入内存中的大型(嵌套)类似Data.Map的结构中。 嵌套映射是值严格的,并且映射内的所有值都是数据类型,其所有字段也都严格。 我花了很多时间来确保不会留下任何未经评估的重击。 导入(取决于我的系统负载)大约需要5-30分钟。 奇怪的是,连续运行中的波动比我预期的要大得多,但这是一个不同的问题。
大数据结构位于“ TVar”内部,由Snap服务器派生的所有客户端线程共享。 客户可以使用一种小的查询语言来请求数据的任意部分。 数据请求量通常很小(最多300kb左右),并且只涉及数据结构的一小部分。 所有只读请求均使用“ readTVarIO”完成,因此它们不需要任何STM事务。
服务器使用以下标志启动:+ RTS -N -I0 -qg -qb。 这将以多线程模式启动服务器,禁用空闲时间和并行GC。 这似乎大大加快了该过程。
服务器大部分运行正常。 但是,不时有一个客户请求超时,CPU峰值达到100%(甚至超过100%),并保持很长一段时间。 同时,服务器不再响应请求。
我能想到的原因很少,可能会导致CPU使用率下降:
该请求仅花费大量时间,因为有很多工作要做。 这有点不太可能,因为有时它会发生在先前运行中被证明非常快的请求中(我指的是20-80ms左右)。
在处理数据并将其发送到客户端之前,仍然需要计算一些未评估的重击。 这也是不太可能的,原因与上一点相同。
垃圾回收以某种方式启动,并开始扫描我的整个5GB堆。 我可以想象这会花费很多时间。
问题是我不知道如何弄清楚到底发生了什么以及如何处理。 因为导入过程花费了很长时间,所以分析结果对我没有任何帮助。 似乎没有办法从代码中有条件地打开和关闭探查器。
我个人怀疑GC是这里的问题。 我正在使用GHC7,它似乎有很多选项可以调整GC的工作方式。
当使用通常具有非常稳定的数据的大堆时,您建议使用哪些GC设置?
猜你喜欢: