Haskell Snap用于大型HTTP响应主体的内存使用

时间:2015-08-09 07:12:03

标签: haskell memory-leaks haskell-snap-framework

我尝试使用Haskell Snap框架提供大量HTTP响应,但内存使用量与响应大小成比例增长。这里有几个使用大型惰性ByteString的简化测试用例:

import Snap.Core (Snap, writeLBS, readRequestBody)
import Snap.Http.Server (quickHttpServe)
import Control.Monad.IO.Class (MonadIO(liftIO))
import qualified Data.ByteString.Lazy.Char8 as LBS (ByteString, length, replicate)

main :: IO ()
main = quickHttpServe $ site test1 where
    test1, test2 :: LBS.ByteString -> Snap ()

    -- Send ss to client
    test1 = writeLBS

    -- Print ss to stdout upon receiving request
    test2 = liftIO . print

    site write = do
        body <- readRequestBody 1000
        -- Making ss dependant on the request stops GHC from keeping a
        -- reference to ss as pointed out by Reid Barton.
        let bodyLength = fromIntegral $ LBS.length body
        write $ ss bodyLength

    ss c = LBS.replicate (1000000000000 * (c + 1)) 'S'
  • test1将ss发送给客户端。内存使用量与ByteString的大小成比例增长。
  • test2在收到请求后将ss打印到Snap monad堆栈中的stdout。这会在很小的内存量下运行。

使用分块编码传递响应,因此我认为Snap也应该能够在常量内存中传递响应。有没有办法实现这个目标?值得注意的是,响应立即开始传递。我尝试过使用transformRequestBody,但遇到了同样的问题。

使用&#34; top&#34;测量记忆。在Linux上,观察到稳定增长到15GB驻留,此时它刚刚开始交换。请求完成后,因此内存使用量比ByteString的大小少几个数量级。一旦请求完成,它就坐在15Gb的居民身边。当我向它发出另一个请求时,它仍然稳定在15Gb,并像以前一样完成了请求。虚拟大小保持在居民规模的5%之内。

首先发现2个并发请求导致虚拟和常驻内存下降到大约5Gb,然后增加到大约17Gb,此时机器无法使用,所以我终止了这个过程。

GHC版本7.8.3

Snap版本0.14.0.5

0 个答案:

没有答案