类似于this related question ,我想在向量上执行并行映射,但在我的例子中,我有一个嵌套向量,并且我似乎无法获得正确的评估。
这是我到目前为止所拥有的:
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import Data.Vector.Strategies
import Control.DeepSeq
main = do
let res = genVVec 200 `using` parVector 2
print res
genUVec :: Int -> U.Vector Int
genUVec n = U.map (ack 2) $ U.enumFromN n 75
genVVec :: Int -> V.Vector (U.Vector Int)
genVVec n = V.map genUVec $ V.enumFromN 0 n
ack :: Int -> Int -> Int
ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))
instance (NFData a, U.Unbox a) => NFData (U.Vector a) where
rnf = rnf . U.toList
给出:
$ ./vectorPar +RTS -N8 -s >/dev/null
SPARKS: 200 (17 converted, 183 pruned)
Total time 1.37s ( 1.30s elapsed)
$ ./vectorPar +RTS -s >/dev/null
SPARKS: 200 (0 converted, 200 pruned)
Total time 1.25s ( 1.26s elapsed)
我还尝试修改 parVector
函数 in vector-strategies直接,但我的尝试是笨拙且无效的。
我意识到 REPA 是为嵌套工作负载而设计的,但这似乎是一个足够简单的问题,我宁愿不必重写大量代码。
最佳答案
注意:这里是矢量策略的有罪作者(这是一个非常小的标题,因为这只是一个我认为其他人会发现有用的黑客功能)。
您认为 parVector
是错误的,因为它允许在使用之前对 Spark 进行 GC 处理,这似乎是正确的。 SimonM 的建议意味着我必须精确地做我试图避免的事情,以一定的成本构建一个新的向量来代替旧的向量。知道这是必要的,没有理由不将 parVector
更改为更简单的定义:
parVector2 :: NFData a => Int -> Strategy (V.Vector a)
parVector2 n = liftM V.fromList . parListChunk n rdeepseq . V.toList
请注意,John L 给出的修复之所以有效,是因为它通过在收集发生之前强制进行计算来“击败”收集器。
我将更改矢量策略库,因此这是不必要的 - 使您的原始代码正常工作。不幸的是,这将产生上述构建新 Vector 的成本(通常很小)。
关于Haskell 嵌套向量并行策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6750297/