Я не думаю, что список является правильным типом данных для этого. Поскольку это просто связанный список, данные обязательно будут доступны последовательно. Хотя вы можете оценивать предметы параллельно, вы не сильно выиграете на этапе сокращения. Если вам действительно нужен список, я думаю, что лучшая функция будет просто
parFold f = foldl1' f . withStrategy (parList rseq)
или, может быть,
parFold f = foldl1' f . withStrategy (parBuffer 5 rseq)
Если шаг сокращения сложен, вы можете получить выигрыш, разделив список следующим образом:
parReduce f = foldl' f mempty . reducedList . chunkList . withStrategy (parList rseq)
where
chunkList list = let (l,ls) = splitAt 1000 list in l : chunkList ls
reducedList = parMap rseq (foldl' f mempty)
Я позволил себе предположить, что ваши данные равны Monoid
для mempty, если это невозможно, вы можете либо заменить mempty своим собственным пустым типом, либо использовать в худшем случае foldl1'
.
Здесь используются два оператора из Control.Parallel.Strategies
. parList
оценивает все элементы списка параллельно. После этого chunkList
делит список на куски по 1000 элементов. Каждый из этих кусков затем параллельно уменьшается на parMap
.
Вы также можете попробовать
parReduce2 f = foldl' f mempty . reducedList . chunkList
where
chunkList list = let (l,ls) = splitAt 1000 list in l : chunkList ls
reducedList = parMap rseq (foldl' f mempty)
В зависимости от того, как именно распределяется работа, один из них может быть более эффективным, чем другие.
Если вы можете использовать структуру данных, которая имеет хорошую поддержку для индексации (Array, Vector, Map и т. Д.), То вы можете сделать двоичные подразделения для этапа сокращения, который, вероятно, будет лучше в целом.