Multithreading Haskell-Control.Parallel中的多核编程
我正在尝试学习如何使用Multithreading Haskell-Control.Parallel中的多核编程,multithreading,haskell,parallel-processing,multicore,Multithreading,Haskell,Parallel Processing,Multicore,我正在尝试学习如何使用Control.Parallel模块,但我认为我没有正确使用它 我正在尝试运行以下代码(fibs.hs) 我是用以下方法编写的: ghc -O2 --make -threaded fibs.hs 然后我执行这个程序得到以下结果(Python脚本的输出,它运行每个程序100次,并返回执行时间的平均和标准偏差): 我的问题是: 当我评估时,到底发生了什么: a `par` (b `pseq` (a + b)) ? 我知道aparb应该提示编译器将a与b并行计算并返回b。
Control.Parallel
模块,但我认为我没有正确使用它
我正在尝试运行以下代码(fibs.hs)
我是用以下方法编写的:
ghc -O2 --make -threaded fibs.hs
然后我执行这个程序得到以下结果(Python脚本的输出,它运行每个程序100次,并返回执行时间的平均和标准偏差):
我的问题是:
a `par` (b `pseq` (a + b)) ?
我知道apar
b应该提示编译器将a与b并行计算并返回b。好啊但是pseq
做什么呢par
允许在另一个线程中计算a
。我在这里猜测,但我认为pseq
的行为很像seq
:它强制首先计算第一个结果(好吧,seq
不一定能做到这一点,但在GHC上它确实做到了)。因此在这种情况下,a
的计算作为一个线程分叉,另一个线程计算b
,然后求和a
和b
Re(2):这是一个非常琐碎的计算,被转移到其他线程;cpu自己计算的速度可能也一样快。我敢打赌,线程的开销对这个简单计算的影响几乎和帮助一样大。您正在创建一个指数级的火花(想想您在这里创建了多少递归调用)。要真正获得良好的并行性,在这种情况下,您需要创建较少的并行工作,因为您的硬件无法处理那么多线程(因此GHC不会生成线程) 解决方案是使用切断策略,如本演讲中所述: 基本上,一旦达到一定深度,就切换到直线版本,并使用+RTS-sstderr查看转换的火花数量,以便确定是否浪费了工作。作为Don,问题是您产生的火花太多。下面是如何重写它以获得良好的加速
import Control.Parallel
cutoff :: Int
cutoff = 20
parFib :: Int -> Int
parFib n | n < cutoff = fib n
parFib n = p `par` q `pseq` (p + q)
where
p = parFib $ n - 1
q = parFib $ n - 2
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
main :: IO ()
main = print $ parFib 40
由于没有人给出关于
pseq
的明确答案,以下是:
语义上与seq相同,但是
有着微妙的操作差异:
seq的两个论点都很严格,
因此编译器可能,例如,
将aseq
b重新排列为bseq
a
seq
b。这通常没有问题
当使用seq表示严格性时,
但是,当
为并行性注释代码,
因为我们需要更多的控制权
评价顺序;我们可能想
在b之前评估a,因为我们知道
b已经被点燃了
与PAR平行。
这就是为什么我们有pseq。相反
对于seq,pseq仅在其功能上严格
第一个参数(就编译器而言)
这限制了
编译器可以执行的转换
这样做,并确保用户可以
保留对评估的控制权
秩序
Haskell不是自动平衡sparks以获得最佳性能吗?它自动平衡线程。运行时具有未计算表达式(sparks)队列,随着工作负载的减少,这些队列将转换为线程。你仍然需要决定不要制造太多的火花(从而浪费时间填满火花队列)
a `par` (b `pseq` (a + b)) ?
import Control.Parallel
cutoff :: Int
cutoff = 20
parFib :: Int -> Int
parFib n | n < cutoff = fib n
parFib n = p `par` q `pseq` (p + q)
where
p = parFib $ n - 1
q = parFib $ n - 2
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
main :: IO ()
main = print $ parFib 40
[computer ~]$ ghc --make -threaded -O2 Main.hs
[1 of 1] Compiling Main ( Main.hs, Main.o )
Linking Main ...
[computer ~]$ time ./Main +RTS -N1
102334155
real 0m1.509s
user 0m1.450s
sys 0m0.003s
[computer ~]$ time ./Main +RTS -N2
102334155
real 0m0.776s
user 0m1.487s
sys 0m0.023s
[computer ~]$ time ./Main +RTS -N3
102334155
real 0m0.564s
user 0m1.487s
sys 0m0.030s
[computer ~]$ time ./Main +RTS -N4
102334155
real 0m0.510s
user 0m1.587s
sys 0m0.047s
[computer ~]$