在尝试在 Haskell 程序中添加多线程功能后,我注意到性能根本没有提高。追着它,我从threadscope得到了以下数据:
绿色表示正在运行,橙色表示垃圾回收。 这里的绿色竖条表示 spark 创建,蓝色条表示并行 GC 请求,浅蓝色条表示线程创建。 标签是:spark created、requesting parallel GC、create thread n、steak from cap 2。
平均而言,我在 4 个内核上只获得了大约 25% 的活动,这与单线程程序相比根本没有任何改进。
当然,如果没有对实际程序的描述,这个问题将是无效的。本质上,我创建了一个可遍历的数据结构(例如树),然后在其上 fmap 一个函数,然后将其输入图像写入例程(解释在程序运行结束时明确的单线程段,过去 15 秒) . 函数的构造和 fmapping 都需要大量的时间来运行,尽管第二个时间稍长一些。
上面的图表是通过在图像写入消耗该数据结构之前为该数据结构添加一个 parTraversable 策略来制作的。我还尝试在数据结构上使用 toList,然后使用各种并行列表策略(parList、parListChunk、parBuffer),但对于各种参数(即使使用大块),每次的结果都是相似的。
我还尝试在将函数映射到可遍历数据结构之前对其进行全面评估,但发生了完全相同的问题。
以下是一些额外的统计数据(针对同一程序的不同运行):
5,702,829,756 bytes allocated in the heap
385,998,024 bytes copied during GC
55,819,120 bytes maximum residency (8 sample(s))
1,392,044 bytes maximum slop
133 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 10379 colls, 10378 par 5.20s 1.40s 0.0001s 0.0327s
Gen 1 8 colls, 8 par 1.01s 0.25s 0.0319s 0.0509s
Parallel GC work balance: 1.24 (96361163 / 77659897, ideal 4)
MUT time (elapsed) GC time (elapsed)
Task 0 (worker) : 0.00s ( 15.92s) 0.02s ( 0.02s)
Task 1 (worker) : 0.27s ( 14.00s) 1.86s ( 1.94s)
Task 2 (bound) : 14.24s ( 14.30s) 1.61s ( 1.64s)
Task 3 (worker) : 0.00s ( 15.94s) 0.00s ( 0.00s)
Task 4 (worker) : 0.25s ( 14.00s) 1.66s ( 1.93s)
Task 5 (worker) : 0.27s ( 14.09s) 1.69s ( 1.84s)
SPARKS: 595854 (595854 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.00s ( 0.00s elapsed)
MUT time 15.67s ( 14.28s elapsed)
GC time 6.22s ( 1.66s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 21.89s ( 15.94s elapsed)
Alloc rate 363,769,460 bytes per MUT second
Productivity 71.6% of total user, 98.4% of total elapsed
我不确定我可以提供哪些其他有用的信息来帮助回答。分析并没有发现任何有趣的东西:它与单核统计数据相同,只是添加的 IDLE 占用了 75% 的时间,正如上面所预期的那样。
发生了什么阻碍了有用的并行化?