2014-09-07 22 views
11

Desidero leggere tutte le righe di un file di 1 GB di grandi dimensioni il più velocemente possibile in un Stream<String>. Attualmente sto usando Files(path).lines() per quello. Dopo aver analizzato il file, sto facendo alcuni calcoli (map()/filter()) Inizialmente ho pensato che fosse già fatto in parallelo, ma sembra che io abbia torto: Durante la lettura del file così com'è, ci vogliono circa 50 secondi sul mio laptop con doppia CPU. Tuttavia, se divido il file usando i comandi bash e poi li elabro in parallelo, ci vogliono solo circa 30 secondi.Come leggere tutte le righe di un file in parallelo in Java 8

Ho provato le seguenti combinazioni:

  1. singolo file, senza linee parallele() Stream ~ 50 secondi
  2. fila indiana, Files(..).lines().parallel().[...] ~ 50 secondi
  3. due file, senza linee parallele() Strean ~ 30 secondi
  4. due file, Files(..).lines().parallel().[...] ~ 30 secondi

ho eseguito questi 4 più volte con più o meno gli stessi risultati (di 1 o 2 secondi). Lo [...] è una catena di mappe e solo filtro, con un toArray(...) alla fine per attivare la valutazione.

La conclusione è che non vi è alcuna differenza nell'uso di lines().parallel(). Poiché la lettura di due file in parallelo richiede meno tempo, si ottiene un aumento delle prestazioni dalla divisione del file. Tuttavia sembra che l'intero file sia letto in serie.

Modifica: Voglio sottolineare che utilizzo un SSD, quindi è praticamente alla ricerca di tempo. Il file ha 1658652 (relativamente brevi) linee in totale. dividere il file in bash impiega circa 1,5 secondi: time split -l 829326 file # 829326 = 1658652/2 split -l 829326 file 0,14s user 1,41s system 16% cpu 9,560 total

Quindi la mia domanda è: esiste alcuna classe o funzione in Java 8 JDK che può parallelizzare la lettura di tutte le linee, senza dover dividere prima? Ad esempio, se ho due core CPU, , il primo lettore di righe dovrebbe iniziare dalla prima riga e un secondo dalla riga (totalLines/2)+1.

+2

Che cosa intendi _split il file utilizzando bash_? Inoltre, devi leggere il file per trovare le linee. Non può solo sapere dove sono i terminatori di linea. –

+2

@SotiriosDelimanolis È possibile passare a un punto arbitrario nel file e cercare la prima nuova riga e dividere in quel modo senza elaborare prima tutti i dati nell'intero segmento. –

+0

In bash, puoi usare 'split -l # lines', combinalo con' wc -l' per dividerlo in due parti. Micheal Petch ha capito la mia idea :) – user3001

risposta

6

Potresti trovare aiuto da this post. Cercando di parallelizzare la lettura effettiva di un file probabilmente stai abbaiando dall'albero sbagliato, poiché il più grande rallentamento sarà il tuo file system (anche su un SSD).

Se si imposta un canale di file in memoria, si dovrebbe essere in grado di elaborare i dati in parallelo da lì con grande velocità, ma è probabile che non ne avrete bisogno visto che vedrete un enorme aumento di velocità.

+0

Ho cambiato il metodo che ho usato per il test, mi sembra sbagliato, dato che ho accidentalmente limitato la dimensione del risultato a 200 prima di eseguire l'operazione finale (forEach (System.out :: println) sul flusso risultante. (Path) legge il file dato come un flusso sequenziale.Inoltre, la maggior parte del tempo è stato effettivamente dedicato alla lettura del file e non ai calcoli, inoltre mi sono imbattuto in alcuni problemi di GC. Ho cronometrato Files.lines (Path) .toArray() vs un metodo che utilizza un MappedByteBuffer (readonly, 0, file.size) .I risultati erano in realtà lo stesso, MappedByteBuffer un po 'più lento qua e là. – user3001

+0

@ user3001, qual è stata la risoluzione finale qui? – daydreamer

+0

In realtà ho scoperto che in gran parte dipende da cosa fai con i dati nello stream. Alla fine ho usato uno stream sequenziale per evitare problemi con la garbage collection. – user3001

Problemi correlati