【问题标题】:why does this programm consume so much memory?为什么这个程序会消耗这么多内存?
【发布时间】:2021-02-20 11:18:07
【问题描述】:

前段时间我需要使用算法来解决一个 KP 问题,在 haskell 中

这是我的代码的样子:

stepKP :: [Int] -> (Int, Int) -> [Int]
stepKP l (p, v) = take p l ++ zipWith bestOption l (drop p l)
  where bestOption a = max (a+v)


kp :: [(Int, Int)] -> Int -> Int
kp l pMax = last $ foldl stepKP [0 | i <- [0..pMax]] l



main = print $ kp (zip weights values) 20000
       where weights = [0..2000]
             values = reverse  [8000..10000]

但是当我尝试执行它时(在使用 ghc 编译后,没有标志),它似乎很糟糕: 这是命令./kp -RTS -s的结果

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

我认为我的程序占用 O(n*w) 内存,而它可以在 O(w) 中完成。 (w为总容量)

这是懒惰评估占用太多空间的问题,还是其他问题? 这段代码如何更节省内存和时间?

【问题讨论】:

标签: haskell memory lazy-evaluation space-complexity


【解决方案1】:

我们可以将左折叠视为执行迭代,同时保留最后返回的累加器。

当有很多迭代时,一个问题是累加器可能会在内存中变得太大。而且因为 Haskell 是惰性的,即使累加器是像 Int 这样的原始类型也会发生这种情况:在一些看似无辜的 Int 值后面可能潜伏着大量未决操作,以 thunk 的形式。

这里严格的左折叠函数foldl' 很有用,因为它确保在评估左折叠时,累加器将始终保存在weak head normal form (WHNF) 中。

唉,有时这还不够。 WHNF 仅表示评估已发展到值的“最外层构造函数”。这对于Int 来说已经足够了,但对于列表或树等递归类型来说,这并不能说明什么:thunk 可能只是潜伏在列表的更下方,或者在下面的分支中。

这里就是这种情况,累加器是在每次迭代时重新创建的列表。每次迭代,foldl' 只评估列表到_ : _。未评估的maxzipWith 操作开始堆积。

我们需要一种在每次迭代时触发对累加器列表的完整评估的方法,该方法可以清除内存中的任何 maxzipWith thunk。这就是force 所完成的。当force $ something 被评估为WHNF 时,something 被完全评估为normal form,也就是说,不仅到最外层的构造函数,而且“深度”。

请注意,我们仍然需要 foldl' 以便在每次迭代时“触发”force

【讨论】:

  • 顺便说一句,只用力而不用 foldl' 对我来说就足够了......
猜你喜欢
  • 2019-09-30
  • 2018-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
相关资源
最近更新 更多