Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Haskell-Control.Parallel中的多核编程_Multithreading_Haskell_Parallel Processing_Multicore - Fatal编程技术网

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))   ?
    
    我知道a
    par
    b应该提示编译器将a与b并行计算并返回b。好啊但是
    pseq
    做什么呢

  • 为什么我会看到如此小的性能提升? 我在一台英特尔四元机上运行这个。我希望使用-N5或-N6运行不会对性能产生真正的影响,或者程序实际上会开始执行得非常糟糕。但是为什么我没有看到从-N2到-N3的改进,为什么最初的改进如此之小

  • Re(1):
    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的两个论点都很严格, 因此编译器可能,例如, 将a
    seq
    b重新排列为b
    seq
    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 ~]$