gpt4 book ai didi

haskell - 为什么 Data.Binary 的 encodeFile 不偷懒?

转载 作者:行者123 更新时间:2023-12-04 14:16:20 25 4
gpt4 key购买 nike

在 GHCI 中,我运行了这个简单的测试:

encodeFile "test" [0..10000000]

这条线运行得非常快(<10 秒),但我的内存使用量在它完成之前飙升至 ~500MB。由于它使用 ByteString.Lazy,因此 encodeFile 不应该是惰性的吗?

编辑:罗马下面的答案很棒!我还想指出 this answer另一个问题,这解释了为什么 Data.Binary 对列表进行严格编码并提供了一个稍微优雅的解决方法。

最佳答案

以下是列表序列化的定义方式:

instance Binary a => Binary [a] where
put l = put (length l) >> mapM_ put l

也就是先序列化列表的长度,再序列化列表本身。

为了找出列表的长度,我们需要评估整个列表。
但是我们不能对它进行垃圾收集,因为第二个需要它的元素
部分, mapM_ put l .所以整个列表必须在
在元素序列化开始之前评估长度。

下面是堆配置文件的样子:

profile

注意在构建列表以计算其长度时它是如何增长的,并且
然后在元素被序列化并且可以被 GC 收集时减少。

那么,如何解决这个问题?在您的示例中,您已经知道长度。那么你
可以编写一个采用已知长度的函数,而不是计算它:
import Data.Binary
import Data.ByteString.Lazy as L
import qualified Data.ByteString as B
import Data.Binary.Put

main = do
let len = 10000001 :: Int
bs = encodeWithLength len [0..len-1]
L.writeFile "test" bs

putWithLength :: Binary a => Int -> [a] -> Put
putWithLength len list =
put len >> mapM_ put list

encodeWithLength :: Binary a => Int -> [a] -> ByteString
encodeWithLength len list = runPut $ putWithLength len list

该程序在 53k 的堆空间内运行。

您还可以在 putWithLength 中包含安全功能。 :在序列化列表时计算长度,并检查最后的第一个参数。如果不匹配,则抛出错误。

练习 : 为什么还需要把长度传给 putWithLength而不是使用如上所述的计算值?

关于haskell - 为什么 Data.Binary 的 encodeFile 不偷懒?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11659986/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com