Wykonałem jakąś intensywną obliczeniowo pracę w F #. Funkcje takie jak Array.Parallel.map
, które korzystają z biblioteki zadań interfejsu .Net, przyspieszyły mój kod wykładniczo dla naprawdę minimalnego wysiłku.F # PSeq.iter wydaje się nie używać wszystkich rdzeni
Jednak ze względu na problemy z pamięcią, przerobiłem część mojego kodu tak, aby można ją było leniwie ocenić w wyrażeniu sekwencji (to znaczy, że muszę przechowywać i przekazywać mniej informacji). Kiedy nadszedł czas, aby ocenić użyłem:
// 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
Zamiast:
// 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
Przy zastosowaniu dowolnej funkcji Array.Parallel mogę wyraźnie zobaczyć wszystkie rdzenie na moim opuszcza komputer w biegu (~ 100% użycie procesora). Jednak wymagana dodatkowa pamięć oznacza, że program nigdy się nie zakończył.
W wersji PSeq.iter po uruchomieniu programu zużywa się tylko około 8% (i minimalne użycie pamięci RAM).
A więc: Czy jest jakiś powód, dla którego wersja PSeq działa znacznie wolniej? Czy to z powodu leniwej oceny? Czy jest jakiś magiczny "be parallel", którego mi brakuje?
Dzięki,
innych zasobów implementacje kodu źródłowego zarówno (wydają się korzystać z różnych bibliotek równolegle 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: Dodano więcej szczegóły dotyczące przykładów kodu i szczegółów
Kod:
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
Array
// 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
Szczegóły:
- przechowywania na częstotliwo Wersja e-array działa szybko (aż do momentu awarii) w czasie poniżej 10 minut, ale wymaga ~ 70 GB pamięci RAM, zanim się zawiesi (64 GB fizycznej, reszta stronicowana).
- Wersja seq zajmuje ponad 34 minuty i wykorzystuje ułamek pamięci RAM (tylko około 30 GB)
- Obliczam ~ miliardów wartości. Stąd miliard podwójnych (po 64 bity) = 7.4505806GB. Są bardziej złożone formy danych ... i kilka niepotrzebnych kopii, które czyszczę, a więc obecne ogromne wykorzystanie pamięci RAM.
- Tak, architektura nie jest wspaniała, leniwy wynik jest pierwszą częścią mnie, która próbuje zoptymalizować program i/lub wsadowe dane na mniejsze porcje.
- W przypadku mniejszego zestawu danych, oba fragmenty kodu wyprowadzają te same wartości. wyniki.
- @pad, próbowałem tego, co sugerujesz, PSeq.iter wydawał się działać poprawnie (wszystkie rdzenie aktywne) po zasileniu Kalkulacją [], ale nadal pozostaje kwestia pamięci RAM (ostatecznie uległa awarii)
- zarówno część podsumowująca kodu, jak i część obliczeniowa są obciążone przez procesor (głównie dlatego, że dużych zbiorów danych)
- z wersją Sekw po prostu dążyć do parallelize raz
Ocena leniwy nie jest przyjemna przy wykonywaniu równoległym. Aby być sprawiedliwym, przekaż te same 'Obliczenia []' do 'PSeq.iter' i' Array.Parallel.map'. Nie można podać przyczyny bez posiadania więcej szczegółów na temat 'Obliczenia' i' niektóreFuncToExtractResults'. – pad
Dziękuję za sugestię, próbowałem tego i PSeq zachowuje się dobrze, gdy otrzymam tablicę zamiast leniwego seq ... jednak nie rozwiązuje problemu z pamięcią –