Haskell 为什么这个程序会占用这么多内存?
不久前,在哈斯克尔,我需要使用一种算法来解决一个KP问题 下面是我的代码的样子:Haskell 为什么这个程序会占用这么多内存?,haskell,memory,lazy-evaluation,space-complexity,Haskell,Memory,Lazy Evaluation,Space Complexity,不久前,在哈斯克尔,我需要使用一种算法来解决一个KP问题 下面是我的代码的样子: stepKP::[Int]->(Int,Int)->[Int] 步骤kp l(p,v)=使用最佳选项l(下降p l)选择p l++zip 其中,选项a=最大值(a+v) kp::[(Int,Int)]->Int->Int kp l pMax=last$foldl stepKP[0 | i我们可以将左折叠视为执行迭代,同时保留最后返回的累加器 当有大量迭代时,一个问题是累加器可能在内存中变得太大。由于Haskell很
stepKP::[Int]->(Int,Int)->[Int]
步骤kp l(p,v)=使用最佳选项l(下降p l)选择p l++zip
其中,选项a=最大值(a+v)
kp::[(Int,Int)]->Int->Int
kp l pMax=last$foldl stepKP[0 | i我们可以将左折叠视为执行迭代,同时保留最后返回的累加器
当有大量迭代时,一个问题是累加器可能在内存中变得太大。由于Haskell很懒惰,即使累加器是原始类型,如Int
:在一些看似无辜的Int
值后面,可能潜伏着大量以thunk形式的挂起操作
这里,严格的左折叠函数foldl'
非常有用,因为它确保在计算左折叠时,累加器始终保持在(WHNF)中
唉,有时候这还不够。WHNF只说求值已上升到值的“最外层构造函数”。这对于Int
,已经足够了,但是对于列表或树之类的递归类型,这并不能说明什么:Thunk可能只是潜伏在列表的更下面,或者在下面的分支中
这里就是这种情况,累加器是一个在每次迭代时重新创建的列表。每次迭代,foldl'
只对列表进行求值,直到:\u
。未求值的max
和zipWith
操作开始堆积
我们需要的是一种在每次迭代时触发对累加器列表的完整计算的方法,它可以清除内存中的任何max
和zipWith
thunks。这就是我们要完成的。当force$something
被计算到WHNF时,something
被完全计算到,也就是说,不仅仅是到outermost构造函数,但“深度”
请注意,我们仍然需要foldl'
,以便在每次迭代时“触发”强制。foldl
->foldl'
?我确实尝试过,但这没有什么区别……另一个快速尝试的方法是启用优化进行编译。@rambi将使用[10000999..8000]
而不是reverse[8000..10000]
。reverse总是需要具体化整个列表。而且splitAt
会比单独使用take
和drop
好,甚至更糟!顺便说一句,对我来说,只使用武力而不使用foldl就足够了。。。
1980100
9,461,474,416 bytes allocated in the heap
6,103,730,184 bytes copied during GC
1,190,494,880 bytes maximum residency (18 sample(s))
5,098,848 bytes maximum slop
2624 MiB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 6473 colls, 0 par 2.173s 2.176s 0.0003s 0.0010s
Gen 1 18 colls, 0 par 4.185s 4.188s 0.2327s 1.4993s
INIT time 0.000s ( 0.000s elapsed)
MUT time 3.320s ( 3.322s elapsed)
GC time 6.358s ( 6.365s elapsed)
EXIT time 0.000s ( 0.000s elapsed)
Total time 9.679s ( 9.687s elapsed)
%GC time 0.0% (0.0% elapsed)
Alloc rate 2,849,443,762 bytes per MUT second
Productivity 34.3% of total user, 34.3% of total elapsed