繁体   English   中英

在Haskell程序中分析多线程性能 - 没有使用并行策略的加速

[英]Profiling multithreading performance in a Haskell program — no speedups using parallel strategies

在尝试在Haskell程序中添加多线程功能后,我注意到性能根本没有改善。 追逐它,我从threadscope获得了以下数据:

图1 绿色表示正在运行,橙色表示垃圾收集。 图2图3 这里垂直的绿色条表示火花创建,蓝色条表示并行GC请求,浅蓝色条表示创建线程。 图4 标签是:创建spark,请求并行GC,创建线程n,以及从第2章窃取火花。

平均而言,我只能在4个内核上获得大约25%的活动,这与单线程程序相比没有任何改进。

当然,如果没有实际程序的描述,这个问题就会无效。 本质上,我创建了一个可遍历的数据结构(例如树),然后在它上面映射一个函数,然后将其提供给一个图像编写例程(在程序运行结束时解释明确的单线程段,超过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%的时间,正如上面所预期的那样。

发生了什么阻止了有用的并行化?

很抱歉,我无法及时提供代码以协助受访者。 我花了一段时间才解开问题的确切位置。

问题如下:我正在尝试一个函数

f :: a -> S b

在可遍历的数据结构上

structure :: T a

其中S和T是两个可遍历的仿函数。

然后,当使用parTraversable时,我错误地写了

Compose (fmap f structure) `using` parTraversable rdeepseq

代替

Compose $ fmap f structure `using` parTraversable rdeepseq

所以我错误地使用Compose TS的Traversable实例来进行多线程处理(使用Data.Functor.Compose)。

(这看起来应该很容易捕获,但我花了一些时间从代码中提取上述错误!)

现在看起来好多了!

图1

图2

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM