haskell - 我误解了 Haskell 的 Control.Parallel 并想澄清

标签 haskell parallel-processing

我试图弄清楚 Haskell 的 Control.Parallel 模块,我写了以下内容(tMap 是时间比较的基本案例):

import Control.Parallel

paraMap, tMap :: (a -> b) -> [a] -> [b]

paraMap _ [] = []
paraMap f [x] = [f x]
paraMap f (x : xs@(y : ys)) = (f y `par` f x) : paraMap f xs

tMap _ [] = []
tMap f (x : xs) = f x : tMap f xs

这个想法是 paraMap 将在完成当前元素之前开始计算下一个元素。我的测试表明它的性能比 tMap 差。我猜这是因为 par 引入了更多开销,并且因为我没有正确使用它,它没有产生足够的 yield 来克服成本。

我上面的例子有什么问题?据我了解,x 'par' y基本上意味着“我需要 x 稍后,所以并行计算它,但现在返回 y”(类似于 seq 的工作方式,但体现在 spark 中,这允许它有可能在单独的线)。

我唯一能想到的就是它被混淆了,因为我需要函数的单独实例中的下一个元素(递归)。我想我可以制作 paraMap实际递归的包装,并显式传递下一个 par 'd 元素到下一个递归;但这似乎很笨拙。

我尝试用 :! ghc -o -threaded <fileName> "<fileName><fileExt>" +RTS -s 编译它来调试它(格式化为 GHCi 中的工具),我的主要内容如下:
main = print $ paraMap (\x -> foldl (+) 0 [1..x * 100]) [1..500]

并收到以下反馈:
75,657,304 bytes allocated in the heap
      32,738,472 bytes copied during GC
       8,926,880 bytes maximum residency (7 sample(s))
         139,208 bytes maximum slop
              19 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0        82 colls,     0 par    0.02s    0.04s     0.0005s    0.0199s
  Gen  1         7 colls,     0 par    0.08s    0.12s     0.0168s    0.0587s

  TASKS: 5 (1 bound, 4 peak workers (4 total), using -N1)

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

  INIT    time    0.00s  (  0.15s elapsed)
  MUT     time    0.05s  (  0.30s elapsed)
  GC      time    0.09s  (  0.16s elapsed)
  EXIT    time    0.02s  (  0.01s elapsed)
  Total   time    0.16s  (  0.62s elapsed)

  Alloc rate    1,614,022,485 bytes per MUT second

  Productivity  40.0% of total user, 10.1% of total elapsed

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

除非我读错了,否则没有产生 Spark ,这绝对是一个问题。

我在想这完全错了吗?任何见解将不胜感激。

最佳答案

在这里,par原因 f y被激发,但它的结果从未被使用(它没有绑定(bind)到任何东西,所以你无法访问它):

paraMap f (x : xs@(y : ys)) = (f y `par` f x) : paraMap f xs

下次申请fy (这将在对 f x 的递归调用中以 paraMap 的形式发生)对此引发的 f y 一无所知计算,因为您从未将其传递给它。

一个更简单的例子,遇到同样的问题,就像
foo x y = bar x `par` bar y `pseq` bar x + bar y

同样,第二次调用 bar无法访问已经触发的计算结果。相反,你应该写
foo x y = let x' = bar x
              y' = bar y
          in x' `par` y' `pseq` x' + y'

关于haskell - 我误解了 Haskell 的 Control.Parallel 并想澄清,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26837820/

相关文章:

json - 使用 Pipes.Aeson 在 Haskell 中对 JSON 进行流式解析

haskell - 如何使用泛型提取特定类型的所有值?

javascript - 如何在严格评估的语言中实现 protected 递归?

multithreading - 纯函数式数据结构总是无锁的吗?

java - 处理大量数据并将其写入文件的高效且最快的方法

c++ - 将 openMP 与 SETS 结合使用

scala - Spark如何并行读写多个表?

haskell - Foldl 与 Foldr 内存使用情况

java - JVM 是否具有检测并行化机会的能力?

c++ - 我们如何并行运行算法的 n 个实例并以有效的方式计算结果函数的平均值?