Я занимаюсь некоторой вычислительной интенсивностью в F #. Такие функции, как Array.Parallel.map
, которые используют параллельную библиотеку .Net Task, ускорили мой код экспоненциально для действительно весьма минимальных усилий.F # PSeq.iter, похоже, не использует все ядра
Однако из-за проблем с памятью я переделываю раздел своего кода, чтобы его можно было лениво оценить внутри выражения последовательности (это означает, что я должен хранить и передавать меньше информации). Когда пришло время, чтобы оценить я использовал:
// processor and memory intensive task, results are not stored
let calculations : seq<Calculation> = seq { ...yield one thing at a time... }
// extract results from calculations for summary data
PSeq.iter someFuncToExtractResults results
Вместо:
// processor and memory intensive task, storing these results is an unnecessary task
let calculations : Calculation[] = ...do all the things...
// extract results from calculations for summary data
Array.Parallel.map someFuncToExtractResults calculations
При использовании любой из функций Array.Parallel я могу ясно видеть все ядра на моем компьютере пинок в шестерню (~ 100% использования ЦП). Однако требуемая дополнительная память означает, что программа никогда не заканчивается.
С версией PSeq.iter при запуске программы используется только около 8% использования ЦП (и минимальное использование ОЗУ).
Итак: Есть ли причина, по которой версия PSeq работает намного медленнее? Это из-за ленивой оценки? Есть ли какая-то магия «быть параллельной», что мне не хватает?
Спасибо,
Другие ресурсы, реализации исходного кода обоих (они, кажется, используют различные параллельные библиотеки в .NET):
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs
https://github.com/fsharp/powerpack/blob/master/src/FSharp.PowerPack.Parallel.Seq/pseq.fs
EDIT: Добавлено больше подробная информация о примерах и деталях
Код:
Seq
// processor and memory intensive task, results are not stored let calculations : seq<Calculation> = seq { for index in 0..data.length-1 do yield calculationFunc data.[index] } // extract results from calculations for summary data (different module) PSeq.iter someFuncToExtractResults results
Массив
// processor and memory intensive task, storing these results is an unnecessary task let calculations : Calculation[] = Array.Parallel.map calculationFunc data // extract results from calculations for summary data (different module) Array.Parallel.map someFuncToExtractResults calculations
Детали:
- накапливания в intermediat Версия массива e быстро (насколько это возможно до краха) в течение 10 минут, но использует ~ 70 ГБ оперативной памяти до того, как она сработает (64-гигабайтный физический, остальные постранированы)
- SEQ-версия занимает более 34 минут и использует часть ОЗУ (только около 30 ГБ)
- Существует миллиард значений, которые я рассчитываю. Следовательно, миллиард удваивается (по 64 бита каждый) = 7,4505806 ГБ. Есть более сложные формы данных ... и несколько ненужных копий, которые я очищаю, поэтому текущее массовое использование ОЗУ.
- Да, архитектура невелика, ленивая оценка - это первая часть меня, пытающаяся оптимизировать программу и/или доставлять данные на более мелкие куски.
- С меньшим набором данных обе части кода выводятся одинаково Результаты.
- @pad, я попробовал то, что вы предложили, PSeq.Кажется, что он работал нормально (все активные ядра) при подаче Calculation [], но все еще остается вопрос о RAM (он в конечном итоге разбился)
- как сводная часть кода, так и часть вычисления являются интенсивными CPU (в основном потому, что больших массивов данных)
- с версии Seq я просто прицелиться распараллелить раз
Ленивая оценка не играет хорошо с параллельным исполнением. Чтобы быть справедливым, передайте те же 'Расчет []' на 'PSeq.iter' и' Array.Parallel.map'. Невозможно рассказать причину, не имея более подробной информации о 'Расчет'' и' someFuncToExtractResults'. – pad
Спасибо за предложение, я попробовал это, и PSeq хорошо себя ведет, когда задан массив вместо ленивого seq ... однако он не решает проблему с RAM –