【问题标题】:Is there a parallel find in Haskell?Haskell 中是否有类似的发现?
【发布时间】:2015-04-23 05:19:53
【问题描述】:

我有一些我喜欢在 Haskell 中解决的蛮力问题。我的机器有 16 个核心,所以我想稍微加快一下我目前的算法。

我有一个方法“tryCombination”,它返回 Just (String) 或 Nothing。我的循环如下所示:

findSolution = find (isJust)  [tryCombination a1 a2 a3 n z p |
                               a1 <- [600..700],
                               a2 <- [600..700],
                               a3 <- [600..700],
                               n  <- [1..100],
                               ....

我知道有一个特殊的 parMap 可以并行化 map 函数。如果线程真的找到了第一次出现,那么 mapFind 可能会很棘手,因为它是不可预测的。但是有没有类似 mapAny 的东西来加快搜索速度?

编辑:

我使用“withStrategy (parList rseq)”sn-p 重写了代码。状态报告如下所示:

38,929,334,968 bytes allocated in the heap
 2,215,280,048 bytes copied during GC
     3,505,624 bytes maximum residency (795 sample(s))
       202,696 bytes maximum slop
            15 MB total memory in use (0 MB lost due to fragmentation)

                                  Tot time (elapsed)  Avg pause  Max pause
Gen  0     44922 colls, 44922 par   37.33s    8.34s     0.0002s    0.0470s
Gen  1       795 colls,   794 par    7.58s    1.43s     0.0018s    0.0466s

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

TASKS: 10 (1 bound, 9 peak workers (9 total), using -N8)

SPARKS: 17576 (8198 converted, 9378 overflowed, 0 dud, 0 GC'd, 0 fizzled)

INIT    time    0.00s  (  0.00s elapsed)
MUT     time   81.79s  ( 36.37s elapsed)
GC      time   44.91s  (  9.77s elapsed)
EXIT    time    0.00s  (  0.00s elapsed)
Total   time  126.72s  ( 46.14s elapsed)

Alloc rate    475,959,220 bytes per MUT second

Productivity  64.6% of total user, 177.3% of total elapsed

gc_alloc_block_sync: 834851
whitehole_spin: 0
gen[0].sync: 10
gen[1].sync: 3724

正如我已经提到的(请参阅我的 cmets),所有核心仅工作三秒钟(所有火花都已处理)。 30s以下的所有工作都是由单核完成的。我怎样才能进一步优化?

更多编辑:

我现在尝试了“withStrategy (parBuffer 10 rdeepseq)”,并调整了不同的缓冲区大小:

Buffersize    GC work Balance     MUT     GC
       10              50%     11,69s  0,94s
      100              47%     12,31s  1,67s
      500              40%     11,5 s  1,35s
     5000              21%     11,47s  2,25s

首先我可以说,与没有任何多线程的 59 相比,这是一个很大的改进。第二个结论是,缓冲区大小应尽可能小但大于内核数。 但最好的是,我不再溢出或熄灭火花。全部转换成功。

【问题讨论】:

  • 我会看的地方是unamb 包。 unambs 函数看起来很有希望。
  • 听起来很有趣,但我无法想象在这种特殊情况下如何应用 unamb。你手头有sn-p吗?
  • 不。从来没用过。

标签: haskell parallel-processing


【解决方案1】:

根据 tryCombination 的惰性和所需的并行化,其中之一可能会满足您的需求:

import Control.Parallel.Strategies
findSolution =
    find (isJust) $
    withStrategy (parList rseq) $
    [ tryCombination a1 a2 a3 n z p
    | a1 <- [600..700]
    , a2 <- [600..700]
    , a3 <- [600..700]
    , n  <- [1..100]]

这将tryCombination 执行的工作并行化,以确定它是Just 还是Nothing,而不是Just 中的实际结果。

如果没有这种懒惰被利用并且结果类型很简单,那么写起来可能会更好

findSolution =
    find (isJust) $
    withStrategy (parList rdeepseq) $
    [ tryCombination a1 a2 a3 n z p
    | a1 <- [600..700]
    , a2 <- [600..700]
    , a3 <- [600..700]
    , n  <- [1..100]]

【讨论】:

  • 看起来不错。我对这两个版本进行了一些测试,发现运行时之间没有区别。使用四个线程 (-N4) 只需要一半的程序时间,使用四个以上的线程不会进一步显着减少运行时间。在监视任务窗口时,我可以看到,一开始程序完全吃掉了 16 个内核中的 4 个。但是随后 cpu 使用率下降到只有 5%,尽管我没有 IO 操作。奇怪....看来我必须安装threadscope才能进一步分析....
  • 如果rdeepseqrseq 没有区别,那么tryCombination 很可能在确定某事是否是解决方案时就可以使用该解决方案。
  • 我大幅减少了组合参数集(只是为了在大约一分钟内得到结果),因此无法解决。所以我清楚地看到了它是如何使用所有内核的。但是现在我使用 threadscope 并发现,产生了 19000 个火花,但只处理了 8000 个火花。这就是为什么只有四秒钟所有核心都被使用,然后只有主核心在剩余的 9000 个火花中处于活动状态。似乎这种并行化仍有一些优化潜力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 2016-02-19
  • 2011-04-12
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 2011-10-14
相关资源
最近更新 更多