Ho una funzione che restituisce un'azione IO,azioni Sequencing IO in parallelo
f :: Int -> IO Int
Vorrei calcolare questa funzione in parallelo per più valori dell'argomento. La mia implementazione ingenuo è stato il seguente:
import Control.Parallel.Strategies
vals = [1..10]
main = do
results <- mapM f vals
let results' = results `using` parList rseq
mapM_ print results'
Il mio ragionamento per questo è stato che il primo mapM
lega qualcosa di tipo IO [Int]
a results
, results'
applica una strategia parallela alla lista contenuta, e il mapM_
, infine, chiede i valori effettivi da stampandoli - ma ciò che deve essere stampato è già stato generato in parallelo, quindi il programma dovrebbe essere parallelo.
Dopo essere stato contento che effettivamente utilizza tutte le mie CPU, ho notato che il programma è meno efficace (come in tempo di parete) quando viene eseguito con +RTS -N8
piuttosto che senza alcun flag RTS. L'unica spiegazione che posso pensare è che il primo mapM
deve eseguire una sequenza - cioè eseguire - tutte le azioni IO già, ma ciò non comporterebbe l'inefficacia, ma rendere l'esecuzione N8
efficace come quella non parallela, perché tutto il lavoro è fatto dal filo conduttore. L'esecuzione del programma con +RTS -N8 -s
produce SPARKS: 36 (11 converted, 0 overflowed, 0 dud, 21 GC'd, 4 fizzled)
, che sicuramente non è ottimale, ma sfortunatamente non riesco a capirci nulla.
Suppongo di aver trovato uno dei trampolini di lancio per principianti nella parallelizzazione di Haskell o all'interno della monade IO. Che cosa sto facendo di sbagliato?
Informazioni di base: f n
è una funzione che restituisce la soluzione per il problema di Project Euler n. Poiché molti di loro hanno dati da leggere, inserisco il risultato nella monade IO. Un esempio di come può sembrare è
-- Problem 13: Work out the first ten digits of the sum of one-hundred 50-digit numbers.
euler 13 = fmap (first10 . sum) numbers
where
numbers = fmap (map read . explode '\n') $ readFile "problem_13"
first10 n
| n < 10^10 = n -- 10^10 is the first number with 11 digits
| otherwise = first10 $ n `div` 10
Il file completo può essere trovato here (E 'un po' lungo, ma le prime funzioni "Euler X" dovrebbe essere sufficiente rappresentante), il file principale dove faccio il parallelismo è this one.
È difficile diagnosticare senza vedere altro. Se lo esegui con '+ RTS -s -N', quali sono le statistiche delle scintille convertite/potate/fizzled? E 'f n 'restituisce un thunk che può effettivamente essere acceso? –
@DanielFischer Ero un po 'titubante a pubblicare il file completo perché è piuttosto lungo (esempi minimi e simili). Ho pensato che il mio errore fosse nel codice parallelo, quindi mi sono concentrato su questo nella mia domanda. Ora ho aggiunto un nuovo paragrafo e anche le statistiche '-s' (che sono terribili). – David
Non sono sicuro che quelli che stanno effettivamente facendo I/O lo distruggerebbero, ma per quelli puri (non usando 'Data.Permute', dato che non ho installato quello), ho ottenuto un aumento di velocità (e più scintille convertite) usando 'parListChunk k' invece di' parList' - anche con 'parListChunk 1', anche se questo chiama' parList'. –