Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance Data.ByteString.Builder周围包装的空间泄漏_Performance_Haskell_Memory - Fatal编程技术网

Performance Data.ByteString.Builder周围包装的空间泄漏

Performance Data.ByteString.Builder周围包装的空间泄漏,performance,haskell,memory,Performance,Haskell,Memory,我在Data.ByteString.Builder周围有一个包装器类型,它允许我跟踪正在构建的ByteString的长度(cf.): 运行此代码需要几秒钟,并消耗大约250MB的内存。使用Builder执行相同的任务要快得多,只需40KB。内存配置文件显示,所有额外空间都被BuildStep和Builder的实例占用,直接使用Builder时不会发生这种情况 是什么让这段代码如此低效?为什么在使用Builder时不会发生这种情况 编辑: Michael在下面的回答让我了解了零件的实际评估方法。

我在Data.ByteString.Builder周围有一个包装器类型,它允许我跟踪正在构建的ByteString的长度(cf.):

运行此代码需要几秒钟,并消耗大约250MB的内存。使用
Builder
执行相同的任务要快得多,只需40KB。内存配置文件显示,所有额外空间都被
BuildStep
Builder
的实例占用,直接使用
Builder
时不会发生这种情况

是什么让这段代码如此低效?为什么在使用
Builder
时不会发生这种情况

编辑:

Michael在下面的回答让我了解了
零件的实际评估方法。
在玩了更多的游戏之后,我用以下方式重写了测试代码:

makeStuff !acc 0 = acc
makeStuff !acc i = makeStuff (acc <> char 'x') (i - 1)
stuff = makeStuff mempty 10000000
-- stuffOld = mconcat $ replicate 10000000 $ char 'x'
main = hPutLBuilder stdout stuff
makeStuff!acc 0=acc
制造!acc i=制造品(acc字符'x')(i-1)
stuff=MakeSuff mempty 10000000
--Stuffld=mconcat$replicate 10000000$char'x'
main=hPutLBuilder标准输出
使用此定义,
Builder
LBuilder
的性能和内存使用完全相同(即可怕:-)。因此,在使用
Builder
时,原始版本看起来非常快,因为编译器可以在编译时以某种方式将
mconcat$replicate n$char c
重写为类似
B.lazyByteString$L.replicate n(toAscii c)
的内容,而不是在堆上运行时编写10000000个函数。我试图通过查看生成的核心来确认这一点。我可以说:

  • stuffOld
    的定义是对相对较短的函数的调用,该函数对
    Data.ByteString.Builder.Internal
    中的类型执行某些操作
  • stuff
    的定义是调用
    makeStuff
  • 所说的核心并不意味着只有凡人才能理解

因此,我猜这只是一个病态的基准测试,我的应用程序中的实际性能问题在其他地方。

一个问题是强制计算
mconcat部分
表达式强制计算其
lbLength
,这反过来将强制计算所有单个
char'x'
值,这就是空间泄漏的原因。但是,我发现要使代码的性能与原始的
Builder
相同,唯一的方法是在
B.Builder
周围使用
newtype
。甚至只是
datalbuilder=LBuilder!B.Builder
引入了大量的开销。

Hm.
char'x'
真的需要多次评估吗?换句话说,是否真的存在单独的
char'x'
值,或者只有一个值被多次引用?这不是一个容易回答的问题,它与GHC的共享规则有关。将
char'x'
提升到顶级定义可能会强制共享该值。
parts = replicate 10000000 $ char 'x'
main = hPutLBuilder stdout $ mconcat parts
makeStuff !acc 0 = acc
makeStuff !acc i = makeStuff (acc <> char 'x') (i - 1)
stuff = makeStuff mempty 10000000
-- stuffOld = mconcat $ replicate 10000000 $ char 'x'
main = hPutLBuilder stdout stuff