Сумма префиксов параллельных данных Haskell

Я играю с кодом Data Parallel Haskell и обнаружил, что мне нужна сумма префикса. Однако я не видел ни одного базового оператора в dph package для суммы префикса.

Я накатил свой собственный, но, поскольку я новичок в dph, я не уверен, правильно ли он использует преимущества параллелизма:

{-# LANGUAGE ParallelArrays #-}
{-# OPTIONS_GHC -fvectorise #-}

module PrefixSum ( scanP ) where
import Data.Array.Parallel (lengthP, indexedP, mapP, zipWithP, concatP, filterP, singletonP, sliceP, (+:+), (!:))
import Data.Array.Parallel.Prelude.Int ((<=), (-), (==), Int, mod)
-- hide prelude
import qualified Prelude 

-- assuming zipWithP (a -> b -> c) given 
-- [:a:] of length n and
-- [:b:] of length m, n /= m
-- will return
-- [:c:] of length min n m

scanP :: (a -> a -> a) -> [:a:] -> [:a:]
scanP f xs = if lengthP xs <= 1
                then xs
                else head +:+ tail
  where -- [: x_0, x_2, ..., x_2n :]
        evens = mapP snd . filterP (even . fst) $ indexedP xs
        -- [: x_1, x_3 ... :]
        odds = mapP snd . filterP (odd . fst)  $ indexedP xs
        lenEvens = lengthP evens
        lenOdds = lengthP odds
        -- calculate the prefix sums [:w:] of the pair sums [:z:]
        psums = scanP f $ zipWithP f evens odds
        -- calculate the total prefix sums as 
        -- [: x_0, w_0, f w_0 x_2, w_1, f w_1 x_4, ..., 
        head = singletonP (evens !: 0)
        body = concatP . zipWithP (\p e -> [: p, f p e :]) psums $ sliceP 1 lenOdds evens
        -- ending at either
        --    ... w_{n-1}, f w_{n-1} x_2n :]
        -- or
        --    ... w_{n-1}, f w_{n-1} x_2n, w_n :]
        -- depending on whether the length of [:x:] is 2n+1 or 2n+2
        tail = if lenEvens == lenOdds then body +:+ singletonP (psums !: (lenEvens - 1)) else body

-- reimplement some of Prelude so it can be vectorised
f $ x = f x
infixr 0 $
(.) f g y = f (g y)

snd (a,b) = b
fst (a,b) = a

even n = n `mod` 2 == 0
odd n = n `mod` 2 == 1

person rampion    schedule 31.01.2012    source источник
comment
Хм, это вообще параллелизуется? Кажется, довольно серийная идея, но, возможно, я что-то упускаю.   -  person luqui    schedule 31.01.2012
comment
@luqui: параллельный алгоритм суммирования префиксов для массива размером n требует O(log n) параллельных раундов вычислений. Есть две фазы. На прямой фазе, учитывая {a_i | i \in [0,2n-1] }, вы вычисляете { a_2i + a_{2i+1} | i \in [0,n-1] }, используя n/2 параллельных сложений. В обратной фазе, учитывая { \sum_0^{2i+1} a_j | i \in [0,n-1] } и { a_i | i \in [0,2n-1] }, вы вычисляете { \sum_0^i a_j | i \in [0, 2n-1 }, используя n/2 параллельных сложений.   -  person rampion    schedule 31.01.2012
comment
@luqui: естественно, это правильно работает только для ассоциативного +, поскольку существует неотъемлемое предположение, что (a_0 + a_1) + (a_2 + a_3) == ((a_0 + a_1) + a_2) + a_3   -  person rampion    schedule 31.01.2012
comment
Для обычного массива это будет scanl, но я не вижу этого в dph-par.   -  person Louis Wasserman    schedule 01.02.2012
comment
правильно. scanl — это O(n) работы за O(n) времени, scanP — O(n) работы за O(log n) времени.   -  person rampion    schedule 01.02.2012
comment
Ну... согласуются ли тесты с вашими ожидаемыми цифрами? Это не лучший, но, безусловно, жизнеспособный способ определить, выполняете ли вы работу параллельно.   -  person Thomas M. DuBuisson    schedule 01.02.2012


Ответы (1)


Параллельное сканирование префиксов поддерживается, на самом деле они довольно фундаментальны. Так что просто передайте (+) в качестве ассоциативного оператора.

person Don Stewart    schedule 03.05.2012