Haskell在并行计算时速度较慢。

4
我一直在尝试使用并行Haskell程序。令我惊讶的是,我的大多数尝试都使得我的示例运行速度更慢,因此我决定深入研究Threadscope,并开始按照这里的教程进行跟踪。
第5节中介绍了示例程序sudoku3,它被认为是一个良好并行化的Haskell程序示例。因此,我按照教程中的说明进行编译。
ghc -O2 sudoku3.hs -threaded -rtsopts -eventlog

并且(与教程中所描述的不同)测量了它在有和没有并行处理的情况下的速度。结果令人惊讶:

$ ./sudoku3 sudoku17.1000.txt +RTS -s -N1
   1,181,490,684 bytes allocated in the heap
      13,247,408 bytes copied during GC
       1,094,432 bytes maximum residency (5 sample(s))
          35,556 bytes maximum slop
               3 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      2282 colls,     0 par    0.07s    0.08s     0.0000s    0.0061s
  Gen  1         5 colls,     0 par    0.01s    0.01s     0.0012s    0.0044s

  TASKS: 3 (1 bound, 2 peak workers (2 total), using -N1)

  SPARKS: 1000 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 1000 fizzled)

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    2.51s  (  2.90s elapsed)
  GC      time    0.08s  (  0.08s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    2.59s  (  2.98s elapsed)

  Alloc rate    469,886,714 bytes per MUT second

  Productivity  97.0% of total user, 84.3% of total elapsed

gc_alloc_block_sync: 0
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

这里是双核运行的情况:

$ ./sudoku3 sudoku17.1000.txt +RTS -s -N2
   1,207,033,704 bytes allocated in the heap
      23,422,808 bytes copied during GC
       1,066,716 bytes maximum residency (22 sample(s))
          47,524 bytes maximum slop
               5 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      1488 colls,  1488 par    0.40s    0.40s     0.0003s    0.0147s
  Gen  1        22 colls,    21 par    0.07s    0.06s     0.0026s    0.0087s

  Parallel GC work balance: 39.57% (serial 0%, perfect 100%)

  TASKS: 4 (1 bound, 3 peak workers (3 total), using -N2)

  SPARKS: 1000 (968 converted, 0 overflowed, 0 dud, 0 GC'd, 32 fizzled)

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    3.45s  (  2.96s elapsed)
  GC      time    0.47s  (  0.45s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    3.93s  (  3.41s elapsed)

  Alloc rate    349,389,354 bytes per MUT second

  Productivity  88.0% of total user, 101.4% of total elapsed

gc_alloc_block_sync: 913
whitehole_spin: 0
gen[0].sync: 25
gen[1].sync: 0

令人惊讶的是,双核运行速度变慢了。为什么会这样呢?

我的 GHC 版本是 7.6.3
操作系统:Debian Jessie Linux/GNU i386 (i686)


1
并行化(分配任务,收集结果)会带来一些开销,也许你需要一个需要更多计算才能获得好处的问题实例? - mvw
我无法重现这个问题。在我的电脑上,“-N2”几乎快了将近一倍。 - ipsec
1
@mvw 我一开始也是这么想的,但由于我使用的示例是专门为展示并行化效益而创建的示例,如果它不起作用,我会感到惊讶。 - Kritzefitz
你确定你正在比较挂钟时间而不是CPU时间吗?我在尝试复制时错误地查看了CPU时间。使用*nix time命令应该会给出稍微清晰一些的英文结果。编辑:似乎-s也提供了挂钟时间,你的输出确实显示-N2更慢,很奇怪。在Linux x86_64上无法使用ghc 7.8.x进行复制。 - Thomas M. DuBuisson
你可以使用 -s RTS 选项(./foo +RTS -s)来测量 Haskell 程序。它会同时测量墙钟时间和 CPU 时间。由于我已经粘贴了原始结果,你可以查看它们两个。(我猜经过的时间值是墙钟时间。) - Kritzefitz
https://wiki.haskell.org/ThreadScope_Tour/Spark 似乎提供了有关测量的提示。 - mvw
1个回答

2

我刚刚重新测试了一下,和预期的一样,并行版本更快。

事实证明,在CPU 100%忙于转换视频时测量性能是一个坏主意。


-_- 好的,就这样吧。 - Carcigenicate
2
最好使用最低的CPU+磁盘优先级来运行像视频转换这样的任务。我通常会调用 ionice -c3 schedtool -D -n15 -e command args - Petr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接