2012-06-01 10 views
5

L'operazione richiede circa 1 secondoPerché talvolta le raccolte parallele di Scala causano un errore OutOfMemoryError?

(1 to 1000000).map(_+3) 

Mentre questo dà java.lang.OutOfMemoryError: lo spazio di heap Java

(1 to 1000000).par.map(_+3) 

EDIT:

ho Scala di serie 2.9.2 configurazione. Sto scrivendo questo su scala prompt. E in bash posso vedere [-n "$ JAVA_OPTS"] || JAVA_OPTS = "- Xmx256M -Xms32M"

E non ho JAVA_OPTS impostato nel mio indirizzo.

1 milione di numeri interi = 8 MB, lista creando due volte = 16 MB

+0

Potete aggiungere qualche dettaglio in più qui? Con quanta memoria si avvia la JVM, nessun altro switch? Esegui questo nel REPL o lo compili? Se esegui questo dal REPL quante istruzioni hai eseguito prima? L'ho appena provato nel mio REPL e l'ho eseguito 10 volte senza una OOM. – drexin

+0

Funziona bene nel mio REPEL a. – Jan

+0

@Jan per me ha funzionato bene anche nel REPL, ma solo la prima volta ... – Christian

risposta

9

Sembra sicuramente legato al l'opzione di memoria JVM e alla memoria necessaria per immagazzinare una collezione Parralel. Per esempio:

scala> (1 to 1000000).par.map(_+3) 

finisce con un OutOfMemoryError la terza volta ho cercato di valutarlo, mentre

scala> (1 to 1000000).par.map(_+3).seq 

mai fallito. Il problema non è il calcolo è l'archiviazione della collezione Parrallel.

3

Diverse ragioni del fallimento:

  1. collezioni parallele non sono specializzati, in modo gli oggetti vengono inscatolati. Ciò significa che non è possibile moltiplicare il numero di elementi con 8 per ottenere l'utilizzo della memoria.
  2. Utilizzare map significa che l'intervallo viene convertito in un vettore. Per i vettori paralleli non è stata ancora implementata una concatenazione efficiente, quindi la fusione di vettori intermedi prodotti da processori diversi procede per copia - richiedendo più memoria. Questo sarà affrontato nelle versioni future.
  3. Il REPL memorizza i risultati precedenti: l'oggetto valutato in ogni riga rimane in memoria.
+0

È possibile renderli specializzati un giorno? –

+0

Una volta implementata la specializzazione dell'array, alcuni tipi di raccolta dovrebbero essere specializzati, sì. – axel22

2

Ci sono due problemi qui, la quantità di memoria richiesta per memorizzare una raccolta parallela e la quantità di memoria richiesta per "passare attraverso" una raccolta parallela.

La differenza può essere visto tra queste due linee:

(1 to 1000000).map(_+3).toList 
(1 to 1000000).par.map(_+3).toList 

Il REPL memorizza le espressioni valutate, ricordo. Sul mio REPL, posso eseguire entrambe queste 7 volte prima di esaurire la memoria. Passare attraverso le esecuzioni parallele utilizza temporaneamente memoria extra, ma una volta eseguita la lista, quell'utilizzo extra viene eliminato.

(1 to 100000).par.map(_+3) 

restituisce un ParSeq [Int] (in questo caso un ParVector), che occupa più spazio di un vettore normale.Questo ho può eseguire 4 volte prima ho esaurito la memoria, che posso eseguire questo:

(1 to 100000).map(_+3) 

11 volte prima ho esaurito la memoria. Quindi le raccolte parallele, se le mantieni in giro occuperanno più spazio.

Per ovviare al problema, è possibile trasformarli in raccolte più semplici come un List prima di restituirli.

Per quanto riguarda il motivo per cui così tanto spazio è occupato da collezioni parallele e perché mantiene i riferimenti a tante cose, non so, ma ho il sospetto views [*], e se pensate che sia un problema, raise an issue for it.

[*] senza alcuna prova reale.

0

ho avuto lo stesso, ma utilizzando un ThreadPool sembra sbarazzarsi del problema per me:

val threadPool = Executors.newFixedThreadPool(4) 
    val quadsMinPar = quadsMin.par 
    quadsMinPar.tasksupport = new ThreadPoolTaskSupport(threadPool.asInstanceOf[ThreadPoolExecutor]) 

ForkJoin per grandi collezioni potrebbe essere la creazione di troppi thread.

Problemi correlati