我正在编写一个网络文件传输应用程序。使用懒惰的ByteString作为中介

import qualified Data.ByteString.Lazy as BSL

从本地文件构造BSL时,请将BSL放入Socket的句柄中:
BSL.readFile filename >>= BSL.hPut remoteH  -- OK

这很好。内存使用率是恒定的。但是要从Socket接收数据,然后写入本地文件:
BSL.hGet remoteH size >>= BSL.hPut fileH bs  -- starts swapping in 1 second

我可以看到内存使用率持续上升,BSL占用了大小的字节的内存。
更糟糕的是,对于超过我的物理内存大小的大大小,操作系统将立即开始交换。

我必须递归接收ByteStrings的段。那没问题。

为什么 BSL 如此表现?

最佳答案

hGet很严格-它立即要求您请求的字节数。这样做是为了促进数据包级别的读取。

但是,hGetContentsN是惰性的,readFile是根据hGetContentsN实现的。

考虑以下两种实现:

hGetContentsN :: Int -> Handle -> IO ByteString
hGetContentsN k h = lazyRead -- TODO close on exceptions
  where
    lazyRead = unsafeInterleaveIO loop

    loop = do
        c <- S.hGetSome h k -- only blocks if there is no data available
        if S.null c
          then do hClose h >> return Empty
          else do cs <- lazyRead
                  return (Chunk c cs)


hGet :: Handle -> Int -> IO ByteString
hGet = hGetN defaultChunkSize

hGetN :: Int -> Handle -> Int -> IO ByteString
hGetN k h n | n > 0 = readChunks n
  where
    STRICT1(readChunks)
    readChunks i = do
        c <- S.hGet h (min k i)
        case S.length c of
            0 -> return Empty
            m -> do cs <- readChunks (i - m)
                    return (Chunk c cs)

关键魔术是hGetContentsN中的惰性。

10-06 02:44