我正在使用以下代码测试该mmultP
功能repa-algorithms-3.2.1.1
(为简洁起见,此处稍作浓缩):
import Data.Array.Repa hiding (map)
import Data.Array.Repa.Algorithms.Matrix (mmultP)
import Control.Monad (replicateM)
import Control.Arrow ((&&&))
import System.Random.MWC (initialize, uniformR)
import Control.Monad.ST (runST)
import Data.Vector.Unboxed (singleton)
import Data.Word (Word32)
-- Create a couple of dense matrices
genRnds :: Word32 -> [Double]
genRnds seed = runST $ do
gen <- initialize (singleton seed)
replicateM (1000 ^ 2) (uniformR (0, 1) gen)
(arr, brr) = head &&& last $ map (fromListUnboxed (Z :. 1000 :. 1000 :: DIM2) . genRnds) [1, 100000]
-- mmultP test
main :: IO ()
main = mmultP arr brr >>= print
并按此处指定,使用编译
ghc mmultTest.hs -Odph -rtsopts -threaded -fno-liberate-case -funfolding-use-threshold1000 -funfolding-keeness-factor1000 -fllvm -optlo-O3 -fforce-recomp
这是线程运行时中的顺序运行:
$ time ./mmultTest +RTS -K100M > /dev/null
real 0m10.962s
user 0m10.790s
sys 0m0.161s
这是一个使用 4 核的版本(在四核 MacBook Air 上运行):
$ time ./mmultTest +RTS -N4 -K100M > /dev/null
real 0m13.008s
user 0m18.591s
sys 0m2.067s
有人对这里发生的事情有任何直觉吗?-N2
对于and ,我也得到了比顺序慢的性能-N3
;每个核心似乎都增加了一些额外的时间。
请注意,我确实观察到一些手动 Repa 矩阵乘法代码的并行性带来了一些小的收益。
更新:
令人费解;我替换main
为
mmultBench :: IO ()
mmultBench = do
results <- mmultP arr brr
let reduced = sumAllS results
print reduced
并删除了对mwc-random
:
(arr, brr) = head &&& last $ map (fromListUnboxed (Z :. 1000 :. 1000 :: DIM2)) (replicate 2 [1..1000000])
具有运行时选项的 Criterion 基准测试-N1 -K100M
产生:
mean: 1.361450 s, lb 1.360514 s, ub 1.362915 s, ci 0.950
std dev: 5.914850 ms, lb 3.870615 ms, ub 9.183472 ms, ci 0.950
并-N4 -K100M
给我:
mean: 556.8201 ms, lb 547.5370 ms, ub 573.5012 ms, ci 0.950
std dev: 61.82764 ms, lb 40.15479 ms, ub 102.5329 ms, ci 0.950
这是一个可爱的加速。我几乎会认为之前的行为是由于将生成的 1000x1000 数组写入标准输出,但正如我所提到的,如果我交换我自己的矩阵乘法代码,我确实观察到那里的并行性增益。还在挠头。