gpt4 book ai didi

haskell - 如何用 Haskell 向量编写并行代码?

转载 作者:行者123 更新时间:2023-12-04 07:44:56 24 4
gpt4 key购买 nike

一方面,在 Haskell 中 Vector a 似乎是用作数字数组的首选类型。甚至还有一个(不完整的)Vector Tutorial .

另一方面, Control.Parallel.Strategies 主要根据 Traversable 定义.向量库不提供这些实例。
Traversable t 的最小完整定义还应该定义 Foldable

traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
sequenceA :: Applicative f => t (f a) -> f (t a)

我不明白 sequenceA可以为 Data.Vector.Unboxed.Vector 定义.那么,用未装箱的向量编写并行代码的最佳方法是什么?定义一些新的临时策略,如 evalVector或使用 parpseq显式或使用普通 Data.Array而不是向量?

附言平原 Array s 可以毫无问题地并行化: https://gist.github.com/701888

最佳答案

这是 parVector 的黑客工作但这对我有用:

import qualified Data.Vector as V
import Control.Parallel.Strategies
import Control.Parallel
import Control.DeepSeq

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))

main = do
let vec = V.enumFromN 1 1000
let res = (V.map (ack 2) vec) `using` parVector
print res

parVector :: NFData a => Strategy (V.Vector a)
parVector vec = eval vec `seq` Done vec
where
chunkSize = 1
eval v
| vLen == 0 = ()
| vLen <= chunkSize = rnf (v V.! 0) -- FIX this to handle chunks > 1
| otherwise = eval (V.take half v) `par` eval (V.drop half v)
where vLen = V.length v
half = vLen `div` 2

并运行此代码:
[tommd@Mavlo Test]$ ghc --make -O2 -threaded t.hs
... dumb warning ...
[tommd@Mavlo Test]$ time ./t +RTS -N1 >/dev/null
real 0m1.962s user 0m1.951s sys 0m0.009s
[tommd@Mavlo Test]$ time ./t +RTS -N2 >/dev/null
real 0m1.119s user 0m2.221s sys 0m0.005s

当我使用 Integer 运行代码时而不是 Int在类型签名中:
[tommd@Mavlo Test]$ time ./t +RTS -N2 >/dev/null

real 0m4.754s
user 0m9.435s
sys 0m0.028s
[tommd@Mavlo Test]$ time ./t +RTS -N1 >/dev/null

real 0m9.008s
user 0m8.952s
sys 0m0.029s

岩石!

编辑:一个更接近你之前尝试的解决方案更干净(它不使用来自三个独立模块的函数)并且效果很好:
parVector :: NFData a => Strategy (V.Vector a)
parVector vec =
let vLen = V.length vec
half = vLen `div` 2
minChunk = 10
in if vLen > minChunk
then do
let v1 = V.unsafeSlice 0 half vec
v2 = V.unsafeSlice half (vLen - half) vec
parVector v1
parVector v2
return vec
else
evalChunk (vLen-1) >>
return vec
where
evalChunk 0 = rpar (rdeepseq (vec V.! 0)) >> return vec
evalChunk i = rpar (rdeepseq (vec V.! i)) >> evalChunk (i-1)

从这个解决方案中学到的东西:
  • 它使用 Eval monad,这是严格的,所以我们肯定会激发一切(与将东西包装在 let 中并记住使用 bang 模式相比)。
  • 与您提出的实现相反,它 (a) 不会构造一个新向量,这很昂贵 (b) evalChunk使用 rpar 强制评估每个元素和 rdeepseq (我不相信 rpar vec 强制任何矢量元素)。
  • 与我的看法相反,slice采用开始索引和长度,而不是开始和结束索引。哎呀!
  • 我们还需要导入Control.DeepSeq (NFData) ,但我已通过电子邮件发送库列表以尝试解决该问题。

  • 性能似乎类似于第一个 parVector这个答案中的解决方案,所以我不会发布数字。

    关于haskell - 如何用 Haskell 向量编写并行代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4186902/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com