2014-04-11 13 views
9

In Scala, per la lettura di un file di testo e caricarlo in un array, un approccio comune èScala file di testo veloce lettura e caricare in memoria

scala.io.Source.fromFile("file.txt").getLines.toArray 

Soprattutto per file molto grandi, c'è un approccio più veloce, forse leggendo prima i blocchi di byte in memoria e poi dividendoli con caratteri di nuova riga? (Vedi Read entire file in Scala per gli approcci comunemente usati.)

Molte grazie.

+2

Nota che 'Source' usa' BufferedSource', che a sua volta usa 'BufferedReader' di Java. Quindi legge già blocchi di dati in memoria - non legge byte per byte. – DNA

+0

@DNA molte grazie per l'osservazione, chiedendo se ci sono (anche) approcci più veloci, forse con java.nio ... – elm

+1

per favore, definire * file molto grandi * e cosa farai con quei dati (dopo la divisione in righe) –

risposta

19

Il problema di prestazioni non ha nulla a che fare con il modo in cui i dati vengono letti. È già bufferizzato. Non succede nulla fino a quando effettivamente iterazioni sulle linee:

// measures time taken by enclosed code 
def timed[A](block: => A) = { 
    val t0 = System.currentTimeMillis 
    val result = block 
    println("took " + (System.currentTimeMillis - t0) + "ms") 
    result 
} 

val source = timed(scala.io.Source.fromFile("test.txt")) // 200mb, 500 lines 
// took 0ms 

val lines = timed(source.getLines) 
// took 0ms 

timed(lines.next) // read first line 
// took 1ms 

// ... reset source ... 

var x = 0 
timed(lines.foreach(ln => x += ln.length)) // "use" every line 
// took 421ms 

// ... reset source ... 

timed(lines.toArray) 
// took 915ms 

Considerando una lettura velocità di 500MB al secondo per il mio disco rigido, il tempo ottimale sarebbe a 400ms per la 200mb, il che significa che non c'è spazio per miglioramenti diversi da non convertire l'iteratore in un array.

A seconda dell'applicazione, è possibile utilizzare direttamente l'iteratore anziché una matrice. Perché lavorare con un array così grande in memoria sarà comunque un problema di prestazioni.


Edit: Dai vostri commenti suppongo, che si desidera trasformare ulteriormente l'array (forse dividere le righe in colonne come hai detto che si sta leggendo un array numerico). In tal caso, consiglio di effettuare la trasformazione durante la lettura. Per esempio:

source.getLines.map(_.split(",").map(_.trim.toInt)).toArray 

è notevolmente più veloce rispetto

source.getLines.toArray.map(_.split(",").map(_.trim.toInt)) 

(Per me è 1.9s invece di 2.5s) perché non trasformare un intero array gigante in un altro, ma solo ogni riga individualmente, finendo in un unico array (utilizza solo metà dello spazio heap). Inoltre, dal momento che la lettura del file è un collo di bottiglia, la trasformazione durante la lettura ha il vantaggio che ciò comporta un migliore utilizzo della CPU.

+0

Molte grazie per le osservazioni, veramente vale la pena; ancora essenziale nel problema è la necessità di caricare un file in memoria, oltre l'elaborazione del file su base per-line. – elm

+0

Ottima risposta. Dovrebbe essere contrassegnato come corretto –

+0

@ user3189923 Vedere la mia modifica per ulteriori informazioni ... –