cro*_*eea 15 monads performance profiling haskell repa
这里有一个基本的monad问题,与Repa无关,还有几个与Repa有关的问题.
我正在使用Repa3工作.我无法获得高效的并行代码.如果我让我的函数返回延迟数组,我会得到极其缓慢的代码,可以很好地扩展到8个内核.该代码每GHC探测器占用超过20GB的内存,并且比基本的Haskell未装箱矢量运行几个数量级.
或者,如果我让所有函数返回Unboxed清单数组(仍然尝试在函数中使用fusion,例如当我执行'map')时,我获得了更快的代码(仍然比使用Haskell未装箱的向量慢)根本没有扩展,实际上随着核心数量的增加会略微变慢.
基于Repa-Algorithms中的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
Run Code Online (Sandbox Code Playgroud)
没有'now',代码编译得很好.使用'now',我收到以下错误:
无法匹配类型
r' with
数组U(Z:.Int)r'`r'是一个刚性类型变量,由fromArray ::(BaseRing mr,Unbox r,Repr tr)=> Arr tr - > CycRingRepa mr的类型签名绑定在C:\ Users\crockeea\Documents\Code\LatticeLib\CycRingRepa.hs:50:1预期类型:CycRingRepa mr实际类型:CycRingRepa m(阵列U DIM1 r)
我不认为 这是我的问题.如果有人可以解释Monad如何在"现在"工作,将会很有帮助.根据我的最佳估计,monad似乎正在创造一个'Arr U(Arr U r)'.我期待'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)
Run Code Online (Sandbox Code Playgroud)
更好地了解何时使用'now'会更有帮助.
另外几个Repa问题:我应该显式调用computeUnboxedP(如在FFT示例代码中),还是应该使用更通用的computeP(因为unbox部分是由我的数据类型推断的)?我应该在数据类型CycRingRepa中存储延迟或清单数组吗?最后我还希望这段代码能够与Haskell Integers一起使用.这是否需要我编写使用U数组以外的其他代码的新代码,还是可以编写为unbox类型创建U数组的多态代码以及为Integers/boxed类型创建其他数组的多态代码?
我意识到这里有很多问题,我很欣赏任何/所有的答案!
这是源代码now
:
now arr = do
arr `deepSeqArray` return ()
return arr
Run Code Online (Sandbox Code Playgroud)
所以它真的只是一个monadic版本deepSeqArray
.您可以使用其中任何一种来强制进行评估,而不是挂在thunk上.这种"评估"与computeP
被调用时强制的"计算"不同.
在你的代码中,now
不适用,因为你不是monad.但在这种情况下deepSeqArray
也无济于事.考虑这种情况:
x :: Array U Int Double
x = ...
y :: Array U Int Double
y = computeUnboxedP $ map f x
Run Code Online (Sandbox Code Playgroud)
由于y
引用x
,我们希望确保x
在开始计算之前计算y
.如果没有,可用的工作将不会在一组线程中正确分配.为了得到这个工作了,这是更好地写y
为
y = deepSeqArray x . computeUnboxedP $ map f x
Run Code Online (Sandbox Code Playgroud)
现在,对于延迟阵列,我们有
deepSeqArray (ADelayed sh f) y = sh `deepSeq` f `seq` y
Run Code Online (Sandbox Code Playgroud)
而不是计算所有元素,这只是确保计算形状,并减少f
到弱头正常形式.
对于清单和延迟阵列,肯定有时间延迟阵列是优选的.
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
Run Code Online (Sandbox Code Playgroud)
这里"extend"通过在一组新维度上复制值来生成新数组.特别是,这意味着
arrRepl ! (Z :. i :. j :. k) == arrRepl ! (Z :. i :. j' :. k)
Run Code Online (Sandbox Code Playgroud)
值得庆幸的是,extend
产生一个延迟阵列,因为经历所有这些复制的麻烦将是一种浪费.
延迟阵列也允许融合的可能性,如果阵列显而易见,这是不可能的.
最后,computeUnboxedP
只是computeP
一个专门的类型.给予computeUnboxedP
明确可能允许GHC优化更好,使代码更清晰一点.
归档时间: |
|
查看次数: |
1143 次 |
最近记录: |