3

ネットワークファイル転送アプリケーションを書いています。中間としてLazyByteStringを使用する

import qualified Data.ByteString.Lazy as BSL

ローカルファイルからBSLを構築する場合は、BSLをソケットのハンドルに配置します。

BSL.readFile filename >>= BSL.hPut remoteH  -- OK

これは正常に機能します。メモリ使用量は一定です。ただし、Socketからデータを受信する場合は、ローカルファイルに書き込みます。

BSL.hGet remoteH size >>= BSL.hPut fileH bs  -- starts swapping in 1 second

メモリ使用量が増え続けていることがわかります。BSLはサイズバイトのメモリを使用します。さらに悪いことに、私の物理メモリサイズを超える大きなサイズの場合、OSはすぐにスワッピングを開始します。

ByteStringsのセグメントを再帰的に受信する必要があります。それは大丈夫です。

なぜBSLはそのように振る舞うのですか?

4

2 に答える 2

4

hGet厳密です-要求したバイト数をすぐに要求します。これは、データのパケットレベルの読み取りを容易にするために行われます。

ただし、hGetContentsN怠惰でありreadFile、の観点から実装されていhGetContentsNます。

2つの実装を検討してください。

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

于 2012-05-18T12:02:26.507 に答える
2

怠惰なバイト文字列の動作について正式に回答することはできませんが、コンジット列挙子など、ある種のストリーミングアプローチを検討することをお勧めします。コンジットを使用すると、次のように記述できます。

import Data.Conduit
import Data.Conduit.Binary

main = do
    let filename = "something"
    remoteH <- getRemoteHandle
    runResourceT $ sourceHandle remoteH $$ sinkFile filename

必要に応じて、ネットワークコンジットなどHandleを使用して、抽象化を完全にバイパスすることもできます。

runResourceT $ sourceSocket socket $$ sinkFile filename
于 2012-05-18T10:06:58.777 に答える