Примечание. Эта запись была полностью переписана 2011-06-10;спасибо Петру за помощь .Также, пожалуйста, не обижайтесь, если я не приму один ответ, так как этот вопрос кажется довольно открытым.(Но, если вы решите это, вы, конечно, отметите галочкой.)
Другой пользователь задал вопрос о распараллеливании сортировки слиянием.Я думал, что напишу простое решение, но, увы, оно не намного быстрее, чем последовательная версия.
Постановка задачи
Сортировка слиянием - это алгоритм "разделяй и властвуй", гдеЛисты вычислений можно распараллелить.
Код работает следующим образом: список преобразуется в дерево, представляющее вычислительные узлы.Затем шаг слияния возвращает список для каждого узла.Теоретически, мы должны увидеть некоторые существенные улучшения производительности, поскольку мы переходим от алгоритма O (n log n) к алгоритму O (n) с бесконечными процессорами.
Первые этапы вычисления распараллеливаются, когда параметр l (уровень) ниже нуля ниже.Это делается с помощью [через переменную strat ] выбора стратегии rpar , в результате чего подвычисления mergeSort 'x будут выполняться параллельно с mergeSort'у .Затем мы объединяем результаты и форсируем их оценку с помощью rdeepseq .
data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show)
instance NFData a => NFData (Tree a) where
rnf (Leaf v) = deepseq v ()
rnf (Node x y) = deepseq (x, y) ()
listToTree [] = error "listToTree -- empty list"
listToTree [x] = Leaf x
listToTree xs = uncurry Node $ listToTree *** listToTree $
splitAt (length xs `div` 2) xs
-- mergeSort' :: Ord a => Tree a -> Eval [a]
mergeSort' l (Leaf v) = return [v]
mergeSort' l (Node x y) = do
xr <- strat $ runEval $ mergeSort' (l - 1) x
yr <- rseq $ runEval $ mergeSort' (l - 1) y
rdeepseq (merge xr yr)
where
merge [] y = y
merge x [] = x
merge (x:xs) (y:ys) | x < y = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
strat | l > 0 = rpar
| otherwise = rseq
mergeSort = runEval . mergeSort' 10
. Оценивая только несколько уровней вычислений, мы должны иметь приличную параллельную сложность связи также - некоторый постоянный коэффициент порядка n .
Results
Получите исходный код 4-й версии здесь [http://pastebin.com/DxYneAaC] и выполнитеэто с помощью следующего для проверки использования потоков или последующих командных строк для тестирования,
rm -f ParallelMergeSort; ghc -O2 -O3 -optc-O3 -optc-ffast-math -eventlog --make -rtsopts -threaded ParallelMergeSort.hs
./ParallelMergeSort +RTS -H512m -K512m -ls -N
threadscope ParallelMergeSort.eventlog
Результаты на 24-ядерном X5680 @ 3,33 ГГц показывают небольшое улучшение
> ./ParallelMergeSort
initialization: 10.461204s sec.
sorting: 6.383197s sec.
> ./ParallelMergeSort +RTS -H512m -K512m -N
initialization: 27.94877s sec.
sorting: 5.228463s sec.
и на моемсобственная машина, четырехъядерный Phenom II,
> ./ParallelMergeSort
initialization: 18.943919s sec.
sorting: 10.465077s sec.
> ./ParallelMergeSort +RTS -H512m -K512m -ls -N
initialization: 22.92075s sec.
sorting: 7.431716s sec.
Проверка результатов в потоковой области показывает хорошее использование для небольших объемов данных.(хотя, к сожалению, ощутимого ускорения нет).Однако, когда я пытаюсь запустить его в больших списках, как указано выше, он использует около 2 процессоров в половину времени.Кажется, что многие искры подрезаются.Он также чувствителен к параметрам памяти, где 256 МБ - это лучшее место, 128 МБ - 9 секунд, 512 - 8,4, а 1024 - 12,3!
Решения, которые я ищу
Наконец, еслиКто-нибудь знает мощные инструменты, чтобы бросить на это, я был бы признателен.(Eden?).Мой основной интерес к параллелизму на Haskell - уметь писать небольшие вспомогательные инструменты для исследовательских проектов, которые я могу использовать на 24 или 80 основных серверах в кластере нашей лаборатории.Поскольку они не являются основной целью исследований нашей группы, я не хочу тратить много времени на эффективность распараллеливания.Так что для меня проще - лучше, даже если я получу только 20% использования.
Дальнейшее обсуждение
- Я заметил, что вторая полоса в Threadsccope иногда зеленая (ср.ее домашняя страница , где вторая полоса, кажется, всегда является сборщиком мусора).Что это значит?
- Есть ли способ обойти сборку мусора?Кажется, это занимает много времени.Например, почему нельзя выполнить разветвление подкомпьютера, вернуть результат в совместно используемую память и затем умереть?
- Есть ли лучший способ (стрелки, аппликативный) для выражения параллелизма?