我正在玩GHCI,并且遇到了这个(对我来说)奇怪的事情。
我试过了:
λ> let fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
λ> fibs
只是为了看看纤维是如何快速生长的,只是为了玩一下。我认为Haskell在计算每个fib时显示它很酷,所以这是一个很好的懒惰示范。
然而,当我看到数字墙放大时,有一些短暂的跳过,列表生成将完全停止。通常我会认为它是计算下一个数字,但它们似乎是随机发生的 - 计算下一个原始数字总是只有一个加法,所以永远不要慢。在一个数字上,ghci暂停了将近一秒钟。
导致这些短暂减速的原因是什么?为什么他们只发生在特定的数字而不是其他数字?这不应该一直很快吗?
答案 0 :(得分:4)
这几乎可以肯定是垃圾收集(GC),可能是更高代(即主要GC)。在创建大量长寿命垃圾的情况下,这些可能会导致严重的暂停。
如果您希望看到这种情况发生,您可以快速执行并检查:
从tmp.hs
开始:
module Main where
fibs = 1:1:zipWith (+) fibs (tail fibs)
main = print $ fibs !! 100000
使用ghc -rtsopts tmp.hs
使用./tmp +RTS -s
454,847,088 bytes allocated in the heap
230,044,816 bytes copied during GC
4,291,856 bytes maximum residency (226 sample(s))
18,080,904 bytes maximum slop
56 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 676 colls, 0 par 0.07s 0.07s 0.0001s 0.0023s
Gen 1 226 colls, 0 par 0.27s 0.28s 0.0012s 0.0084s
INIT time 0.00s ( 0.00s elapsed)
MUT time 0.14s ( 0.14s elapsed)
GC time 0.34s ( 0.35s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 0.48s ( 0.49s elapsed)
%GC time 71.0% (71.6% elapsed)
Alloc rate 3,303,664,644 bytes per MUT second
Productivity 28.9% of total user, 28.8% of total elapsed
这意味着676个小型垃圾收集和226个主要收集,第二代收集所需的时间比未成年人少12倍。请注意,这只是第十万个数字,因此在较高数字时暂停可能会更长。另请注意,GHCI是一名翻译,所以会有相关的减速。
顺便说一句,我确实尝试在启用优化的情况下编译并运行此示例。时间上没有明显差异,记忆/ GC特征几乎相同。
答案 1 :(得分:1)
正如上面的Jan Dvorak评论的那样,可能是GC开始了。您可以使用some runtime options (see section 4.16.3)来查看更改它们是否会影响此行为。
特别是,您可以输出有关GC事件的信息,以了解是否存在问题:
-t [file], - s [file], - S [file], - 机器可读
这些选项产生运行时系统统计信息,例如执行程序和垃圾收集器所花费的时间,分配的内存量,最大大小 堆等等。这三种变体给出了不同的细节层次: -t以与GHC的-Rghc-timing选项相同的格式生成单行输出,-s在程序结束时生成更详细的摘要,-S另外生成有关每个垃圾收集的信息。