Хобрук: Ваш путь к мастерству в программировании

Как применить параллелизм данных к быстрому преобразованию Фурье haskell?

У меня есть код haskell для разрешения быстрого преобразования Фурье, и я хочу применить к нему параллелизм данных. Однако каждая стратегия, которую я использую, порождает слишком много искр, и большинство из них переполняется.

Кто-нибудь знает, как применить хорошую стратегию параллелизма данных в следующем алгоритме:

-- radix 2 Cooley-Tukey FFT

fft::[Complex Float] -> [Complex Float]
fft [a] = [a]
fft as = interleave ls rs
  where
    (cs,ds) = bflyS as
    ls = fft cs
    rs = fft ds

interleave [] bs = bs
interleave (a:as) bs = a : interleave bs as

bflyS :: [Complex Float] -> ([Complex Float], [Complex Float])
bflyS as = (los,rts)
  where
    (ls,rs) = halve as
    los = zipWith (+) ls rs
    ros = zipWith (-) ls rs
    rts = zipWith (*) ros [tw (length as) i | i <- [0..(length ros) - 1]]

halve as = splitAt n' as
  where
    n' = div (length as + 1) 2

-- twiddle factors
tw :: Int -> Int -> Complex Float
tw n k = cis (-2 * pi * fromIntegral k / fromIntegral n)

ПАР МОНАД

Ответ от leftaroundabout очень помог мне понять, как применять параллелизм данных в коде. Однако я изучил монаду par и попытался применить к ней параллелизм задач. Проблема в том, что он работает намного медленнее, чем оригинальный bflyS. Я думаю, что код, который я разработал, слишком дорог для создания потоков по сравнению с относительной работой, которую я делаю. Кто-нибудь знает, как лучше использовать монаду par?

--New Task Parallelism bflyS

bflySTask :: [Complex Float] -> ([Complex Float], [Complex Float])
bflySTask as = runPar $ do
    let (ls, rs) = halve as
    v1<-new
    v2<-new
    let ros = DATA.zipWith (-) ls rs
    let aux = DATA.map  (tw n) [0..n-1]
    fork $ put v1 (DATA.zipWith (+) ls rs)
    fork $ put v2 (DATA.zipWith (*) ros aux)
    los <- get v1
    rts <- get v2   
    return (los, rts)
        where
                n = DATA.length as

  • Какое определение для halve? Это просто splitAt на половину длины? Нам также не хватает определения tw. 12.03.2014
  • Я забыл добавить их, извините за это .. Я только что добавил функции половин и tw в код 12.03.2014
  • Что вы пробовали для добавления параллелизма? Скорее всего, вам нужно проверить длину перед рекурсией и создавать новые искры только в том случае, если длина больше некоторого предела (может быть, 16 или 32?). Но это также может быть проблемой строгости. 12.03.2014
  • Вы пробовали монаду Par из Control.Monad.Par? Я думаю, что было бы довольно легко настроить некоторые параллельные вычисления таким образом. 12.03.2014
  • Я изучил монаду par, но у меня возникли проблемы с созданием потока, так как код теперь работает медленнее... Я отредактировал основной пост с новым кодом, который я разработал с помощью монады Par. 23.03.2014
  • Я не удивлен, что ваш код медленнее с параллелизмом. Как я сразу сказал, основным узким местом, наверное, является память, а не ЦП, потому что вы все делаете со списками. Попробуйте это с типом плотного массива! 23.03.2014
  • Проблема в том, что я не думаю, что смогу изменить используемую структуру данных, потому что моя задача состоит только в том, чтобы запустить этот код, используя параллелизм данных и задач. 23.03.2014

Ответы:


1

Во-первых: здесь нужно много оптимизировать, прежде чем я начну думать о параллелизме:

  • Списки потрясающие, но их непоследовательная модель памяти означает, что они просто не могут обеспечить обходы почти так же быстро, как вы можете достичь с узкими массивами, такими как Data.Vector, потому что вы неизбежно в конечном итоге получите много промахов кеша. Действительно, я редко видел, чтобы основанный на списках алгоритм выигрывал от распараллеливания, потому что они привязаны к памяти, а не к производительности процессора.

  • Ваши крутящиеся коэффициенты вычисляются снова и снова, и здесь вы, очевидно, можете многого добиться с помощью запоминания.

  • Вы продолжаете вызывать length, но это чрезвычайно расточительная функция (O (n) для чего-то, что могло бы быть O (1)). Используйте какой-нибудь контейнер, который, вероятно, обрабатывает длину; списки не предназначены (нам нравится сохранять их способность быть бесконечными).

Само распараллеливание будет довольно простым; Я бы проверил длину, предложенную Джоном Л., действительно, я бы предпочел довольно большой размер, прежде чем зажечь поток, по крайней мере, что-то вроде 256: поскольку производительность, вероятно, становится решающей только при размерах в несколько тысяч, это должно быть достаточно потоков, чтобы ваши ядра были заняты.

import Data.Vector.Unboxed as UBV
import Control.Parallel.Strategies

type ℂ = Complex Float

fft' :: UBV.Vector ℂ -> UBV.Vector ℂ
fft' aₓs = interleave lᵥs rᵥs
 where (lᵥs, rᵥs) = (fft lₓs, fft rₓs)
                     `using` if UBV.length aₓs > 256 then parTuple2 else r0
       (lₓs, rₓs) = byflyS aₓs
11.03.2014
  • Если целью является параллелизм, я бы подумал о работе с repa вместо vector. Тогда вы получаете параллелизм бесплатно. 12.03.2014
  • Новые материалы

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..

    Как я опубликовал свое первое приложение в App Store в 13 лет
    Как все началось Все началось три года назад летом после моего четвертого класса в начальной школе. Для меня, четвертого класса, лето кажется бесконечным, пока оно не закончится, и мой отец..

    Что в лицо
    Очерк о возвращении физиогномики и о том, почему мы должны это приветствовать. История начинается со странной науки. Р. Тора Бьорнсдоттир, Николас О. Рул. Видимость социального класса по..

    Почему шаблоны проектирования и почему нет?
    Сложность — мать всех проблем в программировании. Программное обеспечение должно быть разработано с точки зрения того, кто его поддерживает, а не того, кто его пишет, потому что программное..