【发布时间】:2020-06-29 15:39:20
【问题描述】:
我编写了以下测试应用程序:
main = print $ sum $ map (read . show) [1 .. 10^7]
当我使用和不使用 -N 标志运行它时,我得到以下结果:
$ ghc -O2 -threaded -rtsopts -o test test.hs
...
$ time ./test +RTS -s
50000005000000
real 0m12.411s
user 0m12.367s
sys 0m0.040s
$ time ./test +RTS -s -N12
50000005000000
real 0m22.702s
user 1m14.904s
sys 0m12.608s
似乎 GHC 决定通过将计算分布在不同的核心上来尊重 -N12 标志(结果非常糟糕),但我找不到任何文档说明当代码不这样做时它是如何决定这样做的包含明确的说明。是否有一些我遗漏的文档?
我有 GHC 版本 8.6.5。
垃圾收集统计:
$ ghc -O2 -threaded -rtsopts -o test test.hs
...
$ time ./test +RTS -s
50000005000000
54,332,520,712 bytes allocated in the heap
53,571,832 bytes copied during GC
56,824 bytes maximum residency (2 sample(s))
29,192 bytes maximum slop
0 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 52088 colls, 0 par 0.154s 0.150s 0.0000s 0.0001s
Gen 1 2 colls, 0 par 0.000s 0.000s 0.0001s 0.0001s
TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)
SPARKS: 0(0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.000s ( 0.000s elapsed)
MUT time 12.250s ( 12.249s elapsed)
GC time 0.155s ( 0.151s elapsed)
EXIT time 0.001s ( 0.010s elapsed)
Total time 12.406s ( 12.410s elapsed)
Alloc rate 4,435,169,879 bytes per MUT second
Productivity 98.7% of total user, 98.7% of total elapsed
real 0m12.411s
user 0m12.367s
sys 0m0.040s
$ time ./test +RTS -s -N12
50000005000000
54,332,687,840 bytes allocated in the heap
214,001,248 bytes copied during GC
183,360 bytes maximum residency (2 sample(s))
146,696 bytes maximum slop
0 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 52088 colls, 52088 par 20.219s 0.975s 0.0000s 0.0001s
Gen 1 2 colls, 1 par 0.001s 0.000s 0.0001s 0.0002s
Parallel GC work balance: 0.15% (serial 0%, perfect 100%)
TASKS: 26 (1 bound, 25 peak workers (25 total), using -N12)
SPARKS: 0(0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.007s ( 0.003s elapsed)
MUT time 67.281s ( 21.720s elapsed)
GC time 20.221s ( 0.975s elapsed)
EXIT time 0.002s ( 0.003s elapsed)
Total time 87.511s ( 22.701s elapsed)
Alloc rate 807,549,654 bytes per MUT second
Productivity 76.9% of total user, 95.7% of total elapsed
real 0m22.702s
user 1m14.904s
sys 0m12.608s
【问题讨论】:
-
你真的不应该认为在没有优化的情况下编译的代码是有意义的。它可能没有优化计算中的分配,您会看到尝试在 12 个内核上运行并行 GC 的开销。你可以在
+RTS -s中包含输出以进行快速 GC 统计吗? (编译时需要添加-rtsopts,iirc。) -
@Carl 我在问题中添加了 GC 统计信息。我会尝试看看我是否可以找到一个最小的工作示例来显示使用 -O2 的这种行为,因为即使使用 -O2,我似乎在我的实际应用程序中也遇到了同样的问题。
-
@Carl,我找到了一个适用于 -O2 的示例。我将编辑这个问题,因为我相信另一个问题会太相似,但如果你认为这样更好,我会回复并提出一个新问题。
-
您可以使用 threadScope 实用程序查看实际的核心使用情况。我的猜测是垃圾收集器正在并行化减慢整个过程。您的代码不应并行运行,因为
map函数不会并行运行。此外,在您的-N12执行中创建零火花,这意味着您没有并行运行代码。我认为... -
@lsmor 知道为什么垃圾收集器这么慢,以及如何避免这种情况?在我的实际代码中,我确实并行运行了一些东西,但是性能改进为零,因为这里发生的任何事情都会发生在我的真实代码中。
标签: multithreading haskell parallel-processing ghc