Много параллельных применений последовательного преобразования в репа
В Repa я хотел бы применить определенный d
линейное преобразование параллельно через самое внутреннее измерение моего массива, т. е. по всем векторам "столбцов".
В общем, такое преобразование может быть выражено в виде матрицы M
и каждая запись M*v
это просто скалярное произведение соответствующего ряда M
с v
, Так что я мог бы просто использовать traverse
с функцией, которая вычисляет соответствующее скалярное произведение. Это стоит d^2
Работа.
Тем не менее, мой M
является особенным: он допускает последовательный алгоритм линейной работы. Например, M
может быть нижней треугольной матрицы с 1
по всему нижнему треугольнику. затем M*v
это просто вектор частичных сумм v
(он же "скан"). Эти суммы могут быть вычислены последовательно очевидным образом, но нужно (i-1)
Первая запись результата для вычисления i
Вступление эффективно. (У меня несколько таких M
, все из которых могут быть рассчитаны так или иначе в линейном последовательном времени.)
Я не вижу очевидного способа использования traverse
(или любые другие функции Repa), чтобы воспользоваться этим свойством M
, Это можно сделать? Это будет довольно расточительно использовать d^2
алгоритм работы (даже с большим параллелизмом), когда есть такой быстрый алгоритм линейной работы.
(Я видел некоторые старые сообщения SO (например, здесь), задающие подобные вопросы, но ничего, что вполне соответствует моей ситуации.)
ОБНОВИТЬ
По запросу здесь приведен иллюстративный код для M
который вычисляет частичные суммы (как описано выше). Как я и ожидал, время выполнения (работа) растет суперлинейно в d
второй аргумент экстента массива (ext
). Это связано с тем, что mulM'
только указывает, как вычислить i
ая запись вывода, независимая от всех остальных записей. Хотя в общем размере массива есть алгоритм линейного времени, я не знаю, как это выразить в Repa.
Интересно, если я удалю строку, которая определяет манифест array'
от main
то время выполнения масштабируется только линейно в общем размере массива! Поэтому, когда массивы задерживаются "на всем пути вниз", слияние / оптимизация должны каким-то образом извлекать алгоритм линейной работы, но без какой-либо явной помощи от меня. Это удивительно, но и не очень полезно для меня, потому что на самом деле мне нужно позвонить mulM
на массивах манифеста.
{-# LANGUAGE TypeOperators, ScopedTypeVariables, FlexibleContexts #-}
module Main where
import Data.Array.Repa as R
-- multiplication by M across innermost dimension
mulM arr = traverse arr id mulM'
where mulM' _ idx@(i' :. i) =
sumAllS $ extract (Z:.0) (Z:.(i+1)) $ slice arr (i' :. All)
ext = Z :. (1000000::Int) :. (10::Int) -- super-linear runtime in 2nd arg
--ext = Z :. (10::Int) :. (1000000::Int) -- takes forever
array = fromFunction ext (\(Z:.j:.i) -> j+i)
main :: IO ()
main = do
-- apply mulM to a manifest array
array' :: Array U DIM2 Int <- computeP $ array
ans :: Array U DIM2 Int <- computeP $ mulM array'
print "done"
1 ответ
Вот возможная модифицированная версия вашего кода с использованием отложенных массивов:
{-# LANGUAGE TypeOperators #-}
import Data.Array.Repa as R
mulM :: (Num a, Source r a) => Array r DIM2 a -> Array D DIM2 a
mulM arr = traverse arr id mulM'
where
mulM' _ idx@(i' :. i) =
sumAllS $ extract (Z:.0) (Z:.(i+1)) $ slice arr (i' :. All)
ext :: DIM2
ext = Z :. (1000000::Int) :. (10::Int)
array :: Array D DIM2 Int
array = fromFunction ext (\(Z:.j:.i) -> j+i)
main :: IO ()
main = do
let delayedArray :: Array D DIM2 Int
delayedArray = delay array
result = computeUnboxedS $ mulM delayedArray
print "done"
Обратите внимание, что это упрощенный пример, и вам, возможно, придется адаптироваться и поэкспериментировать с функциями Repa, чтобы полностью отразить желаемый алгоритм линейной работы для вашей конкретной матрицы M. Обязательно обратитесь к документации Repa для получения дополнительной информации о его возможностях и как лучше всего выразить свои вычисления.