2012-03-15 39 views
15

这里有一个基本的monad问题,与Repa无关,再加上几个特定于Repa的问题。修复3'性能和正确使用'现在'

我正在使用Repa3库。我无法获得高效的并行代码。如果我让我的函数返回延迟数组,我得到的代码非常慢,可以扩展到8个内核。这个代码每GHC分析器需要超过20GB的内存,并且比基本的Haskell unboxed向量慢几个数量级。另外,如果我让所有函数都返回Unboxed清单数组(仍尝试在函数中使用融合,例如当我执行'map'时),我得到的代码更快(仍然比使用Haskell unboxed矢量),它根本不会扩展,事实上,随着更多内核的增加,它的速度会变慢。

基于Repa算法中的FFT示例代码,似乎正确的方法是始终返回清单数组。有没有我应该返回延迟数组的情况?

FFT代码还充分利用'now'功能。但是,当我尝试在我的代码中使用它时发生类型错误:

type Arr t r = Array t DIM1 r 
data CycRingRepa m r = CRTBasis (Arr U r) 
        | PowBasis (Arr U r) 

fromArray :: forall m r t. (BaseRing m r, Unbox r, Repr t r) => Arr t r -> CycRingRepa m r 
fromArray = 
    let mval = reflectNum (Proxy::Proxy m) 
    in \x -> 
     let sh:.n = extent x 
     in assert (mval == 2*n) PowBasis $ now $ computeUnboxedP $ bitrev x 

代码无需'now'即可编译好。与“现在”,我得到以下错误:

Couldn't match type r' with Array U (Z :. Int) r' `r' is a rigid type variable bound by the type signature for fromArray :: (BaseRing m r, Unbox r, Repr t r) => Arr t r -> CycRingRepa m r at C:\Users\crockeea\Documents\Code\LatticeLib\CycRingRepa.hs:50:1 Expected type: CycRingRepa m r Actual type: CycRingRepa m (Array U DIM1 r)

我不认为this是我的问题。如果有人能够解释Monad如何在“现在”工作,那将会很有帮助。据我最好的估计,monad似乎在创造一个'Arr U(Arr Ur)'。我期待着一个'Arr U r',它会匹配数据构造函数模式。发生了什么,我该如何解决这个问题?

类型签名是:

computeUnboxedP :: Fill r1 U sh e => Array r1 sh e -> Array U sh e 
now :: (Shape sh, Repr r e, Monad m) => Array r sh e -> m (Array r sh e) 

这将是有帮助的,当它是适合使用“现在”的一个更好的主意。我应该明确地调用computeUnboxedP(如在FFT示例代码中),还是应该使用更通用的computeP(因为unbox部分是由我的数据类型推断的)? 我应该在数据类型CycRingRepa中存储延迟或清单数组吗?最终我还想要这个代码与Haskell整数一起工作。这是否需要我编写使用U数组以外其他代码的新代码,或者我可以编写多形态代码来创建用于unbox类型的U数组以及用于整数/盒装类型的其他数组?

我意识到这里有很多问题,我很欣赏任何/所有的答案!

回答

2

修复3.1不再需要明确使用now。并行计算功能都是一次性的,并自动将deepSeqArray应用于其结果。 repa-examples包还包含一个矩阵乘法的新实现,它演示了它们的用法。

8

这里的源代码now

now arr = do 
    arr `deepSeqArray` return() 
    return arr 

所以这是真的deepSeqArray只是一个单子版本。您可以使用其中任何一种方式强制进行评估,而不是挂在thunk上。这种“评估”与调用computeP时的“计算”不同。

在您的代码中,now不适用,因为您不在monad中。但在这方面deepSeqArray也不会帮助。考虑这种情况:

x :: Array U Int Double 
x = ... 

y :: Array U Int Double 
y = computeUnboxedP $ map f x 

由于yx,我们希望确保x开始计算y前计算。如果没有,那么可用的工作将不会在这个团队中正确分配。为了得到这个工作了,最好写y作为

y = deepSeqArray x . computeUnboxedP $ map f x 

现在,对于延迟阵列,我们有

deepSeqArray (ADelayed sh f) y = sh `deepSeq` f `seq` y 

而不是计算所有元素,这只是确保形状计算,并将f减少为弱头法线形式。

至于明显vs延迟阵列,肯定有时间延迟阵列是可取的。

multiplyMM arr brr 
= [arr, brr] `deepSeqArrays` 
    A.sumP (A.zipWith (*) arrRepl brrRepl) 
where trr    = computeUnboxedP $ transpose2D brr 
     arrRepl   = trr `deepSeqArray` A.extend (Z :. All :. colsB :. All) arr 
     brrRepl   = trr `deepSeqArray` A.extend (Z :. rowsA :. All :. All) trr 
     (Z :. _  :. rowsA) = extent arr 
     (Z :. colsB :. _ ) = extent brr 

这里“扩展”通过跨越一些新的维度集复制值来生成一个新的数组。具体而言,这意味着

arrRepl ! (Z :. i :. j :. k) == arrRepl ! (Z :. i :. j' :. k) 

值得庆幸的是,extend产生延迟阵列,因为它是要经过所有这些拷贝的麻烦浪费。

延迟阵列还允许融合的可能性,如果数组是明显的,这是不可能的。

最后,computeUnboxedP只是computeP与专门类型。明确给予computeUnboxedP可能会使GHC更好地优化,并使代码更清晰一些。