2016-12-17 2 views
0

Вопрос о эффективном параллелизме, который задает новичок Haskell.Укрощение параллелизма в Haskell/GHC

Задача Advent of Code Day 14 связана с созданием хэшей MD5 последовательности целых чисел, ищем первые n целых чисел, которые дают хеши, выполняющие определенные свойства. Я делаю это по существу, создавая хеши, затем фильтруя их.

Я думал, что было бы хорошо попробовать параллелизм, используя несколько ядер для генерации хэшей.

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

md5sequenceS :: [String] 
md5sequenceS = [makeMd5 i | i <- [0..]] 
    where makeMd5 i = stretch $ getHash (salt ++ show i) 
      stretch h0 = foldr (\_ h -> getHash h) h0 [1..2016] 

... и она отлично работает, хотя и медленно, давая ответ примерно через четыре минуты.

Параллельная версия выглядит следующим образом:

md5sequenceS :: [String] 
md5sequenceS = parMap rdeepseq (makeMd5) [0..] 
    where makeMd5 i = stretch $ getHash (salt ++ show i) 
      stretch h0 = foldr (\_ h -> getHash h) h0 [1..2016] 

... то же самое, как и раньше помимо parMap rdeepseq бит. Это не работает нормально: он потребляет всю доступную память на моей машине и по-прежнему не дает ответа через 30 минут времени на стене. Тем не менее, он использует все процессоры полностью.

Что я должен сделать, чтобы укротить этот параллелизм вне контроля?

(Проблема спецификации не дает никакого понятия о том, как много хэшей мне нужно генерировать, но оказывается, что мне нужно около 30 000 целых хэшируются.)

Редактировать включить принято отвечать

стратегия parBuffer может быть использована в качестве

md5sequenceS = withStrategy (parBuffer 100 rdeepseq) $ map (makeMd5) [0..] 
    where makeMd5 i = stretch $ getHash (salt ++ show i) 
      stretch h0 = foldr (\_ h -> getHash h) h0 [1..2016] 

производительности не велика по сравнению с однопоточной версией, но это другой вопрос ...

ответ

3

parMap заставит оценить весь список, который в вашем случае бесконечен.

Вместо использования parMap вы можете использовать другие стратегии, такие как parBuffer, который способен иметь дело с бесконечными списками.

+1

Это объяснило бы это, спасибо! Перезапись параллельной версии как 'md5sequenceS = withStrategy (parBuffer 100 rdeepseq) $ map (makeMd5) [0 ..]' делает трюк. Настенное время теперь 3мин 44 с с 12 ядрами, против 5 минут 55 секунд для однопроцессорной версии, но это еще одна проблема ... –

Смежные вопросы