如何在Haskell中使用并行策略编写嵌套循环问题

如何在Haskell中使用并行策略编写嵌套循环问题,haskell,parallel-processing,Haskell,Parallel Processing,我在玩平行策略,想知道我是否按照正确的方式做了以下事情。Java代码: double x = 0.0; double[] arr = new double[2000]; for (int i = 0; i < arr.length; i++) arr[i] = i; for (int i = 0; i < arr.length; i++) { x += arr[i] * 5; for (int j

我在玩平行策略,想知道我是否按照正确的方式做了以下事情。Java代码:

    double x = 0.0;
    double[] arr = new double[2000];

    for (int i = 0; i < arr.length; i++) 
        arr[i] = i;

    for (int i = 0; i < arr.length; i++) {
        x += arr[i] * 5;

        for (int j = i + 1; j < arr.length; j++)
            x -= arr[j] * 3;
    }
我的问题是:

  • 在这里使用foldl对吗?我想既然所有的计算都需要完成才能得到结果,我应该强制评估

  • 是否有更好的方法使用分段?我可以利用这个问题中的哪些常见模式

  • 还有什么其他策略可以应用于这个问题?另外,只要使用
    par
    seq
    原语就可以进行并行化


以下是我如何将您的Java程序转换为并行Haskell程序:

parCompute ts = sum (computes `using` parListChunk 100 rseq)
  where 
    computes  = zipWith f ts (tail (tails ts))
    f t tls   = 5 * t - 3 * sum tls
首先,是的,在这里引入严格是个好主意。另一方面,GHC也足够聪明,能够发现这一点!事实上,无论您使用
foldl
foldl'
还是简单地使用
sum
,生成的代码都是完全相同的

为了分段评估列表,您可以简单地使用如上所述的分块策略。然而,每个区块所代表的工作量可能相差很大,因此您可以尝试通过在列表末尾制作更大的区块来平衡它。除此之外,我认为这里没有太多的改进空间。

好的,这次让我们使用REPA(常规并行数组),并将其与parListChunk方法进行比较(因为java示例使用的是数组而不是列表):


我希望您会喜欢这种比较。

风格注释:嵌套的where很难看。我不认为这些代码是等价的。代码“x-=arr[j]*3”应用于列表中位置“i”之后的所有项,但在haskell代码中,等效值仅应用于本地块,而不是位置“i”之后的所有值。或者也许我看错了。@Tim:答案是一样的。我已经用Java检查了Haskell的结果。在这种情况下,我很高兴我错了。谢谢。您的解决方案看起来不错,很有用,但我不太确定生成的代码是否与foldl相同,foldl'。foldl’强制在每次应用函数后完全评估累积结果。在某些情况下,它提供了比使用foldl更好的加速,但不确定生成的代码以何种方式相同。感谢您的比较。这绝对有用。我将等待,看看我们是否有其他选择,然后再选择哪一个答案对我来说是最好的。
parCompute ts = sum (computes `using` parListChunk 100 rseq)
  where 
    computes  = zipWith f ts (tail (tails ts))
    f t tls   = 5 * t - 3 * sum tls
module Main where

import Control.Parallel.Strategies
import Data.List (tails)
import System.Environment (getArgs)
import qualified Data.Array.Repa as R
import qualified Data.Array.Repa.Shape as RS

chunksize = 100

parListCompute :: [Int] -> [Int]
parListCompute ts = (computes `using` parListChunk chunksize rseq)
  where
    computes = zipWith f ts (tail (tails ts))
    f t tls  = 5 * t - 3 * sum tls

parRepaCompute :: R.Array R.DIM1 Int -> R.Array R.DIM1 Int
parRepaCompute arr = R.force $ computes
  where
    computes    = R.map f arr
    f x         = 5*x - 3*(sumRest (x+1) 0)
    sumRest x acc | x > (RS.size . R.extent $ arr) = acc
                  | otherwise                      = sumRest (x+1) (acc+x)

main = do
  (s:_) <- getArgs
  case s of
    "1" -> putStrLn . show .sum $ parListCompute l
    "2" -> putStrLn . show . R.sum $ parRepaCompute r
  where l = [1..70000]
        r = R.fromList (R.Z R.:. (length l)) l
~/haskell$ ghc --make nestloop.hs -O2 -rtsopts -threaded 
[1 of 1] Compiling Main             ( nestloop.hs, nestloop.o )
Linking nestloop ...
haskell$ time ./nestloop 1 +RTS -N4
-342987749755000

real    0m5.115s
user    0m19.870s
sys     0m0.170s
~/haskell$ time ./nestloop 2 +RTS -N4
[-342987749755000]

real    0m1.658s
user    0m3.670s
sys     0m0.070s