2009-06-30 15 views
54

Devo scrivere enormi dati nel file di testo [csv]. Ho usato BufferedWriter per scrivere i dati e ci sono voluti circa 40 secondi per scrivere 174 MB di dati. È questa la velocità più veloce in grado di offrire java?Il modo più veloce per scrivere enormi dati nel file di testo Java

bufferedWriter = new BufferedWriter (new FileWriter ("fileName.csv")); 

Nota: Questi 40 secondi sono il tempo di iterazione e andare a prendere i record di risultati pure. :). 174 mb è per 400000 righe nel set di risultati.

+3

Tu non capita di avere anti-virus attivo sulla macchina su cui si esegue questo codice? –

risposta

87

Tu potrebbe provare a rimuovere BufferedWriter e usare direttamente FileWriter. Su un sistema moderno ci sono buone probabilità che tu stia scrivendo comunque nella memoria cache del drive.

Mi ci vogliono nell'intervallo di 4-5 secondi per scrivere 175 MB (4 milioni di stringhe): si tratta di un Dell dual-core da 2,4 GHz con Windows XP con disco Hitachi da 80 GB e 7200 giri al minuto.

Riesci a isolare quanto tempo è il recupero dei record e quanto costa scrivere?

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.io.Writer; 
import java.util.ArrayList; 
import java.util.List; 

public class FileWritingPerfTest { 


private static final int ITERATIONS = 5; 
private static final double MEG = (Math.pow(1024, 2)); 
private static final int RECORD_COUNT = 4000000; 
private static final String RECORD = "Help I am trapped in a fortune cookie factory\n"; 
private static final int RECSIZE = RECORD.getBytes().length; 

public static void main(String[] args) throws Exception { 
    List<String> records = new ArrayList<String>(RECORD_COUNT); 
    int size = 0; 
    for (int i = 0; i < RECORD_COUNT; i++) { 
     records.add(RECORD); 
     size += RECSIZE; 
    } 
    System.out.println(records.size() + " 'records'"); 
    System.out.println(size/MEG + " MB"); 

    for (int i = 0; i < ITERATIONS; i++) { 
     System.out.println("\nIteration " + i); 

     writeRaw(records); 
     writeBuffered(records, 8192); 
     writeBuffered(records, (int) MEG); 
     writeBuffered(records, 4 * (int) MEG); 
    } 
} 

private static void writeRaw(List<String> records) throws IOException { 
    File file = File.createTempFile("foo", ".txt"); 
    try { 
     FileWriter writer = new FileWriter(file); 
     System.out.print("Writing raw... "); 
     write(records, writer); 
    } finally { 
     // comment this out if you want to inspect the files afterward 
     file.delete(); 
    } 
} 

private static void writeBuffered(List<String> records, int bufSize) throws IOException { 
    File file = File.createTempFile("foo", ".txt"); 
    try { 
     FileWriter writer = new FileWriter(file); 
     BufferedWriter bufferedWriter = new BufferedWriter(writer, bufSize); 

     System.out.print("Writing buffered (buffer size: " + bufSize + ")... "); 
     write(records, bufferedWriter); 
    } finally { 
     // comment this out if you want to inspect the files afterward 
     file.delete(); 
    } 
} 

private static void write(List<String> records, Writer writer) throws IOException { 
    long start = System.currentTimeMillis(); 
    for (String record: records) { 
     writer.write(record); 
    } 
    writer.flush(); 
    writer.close(); 
    long end = System.currentTimeMillis(); 
    System.out.println((end - start)/1000f + " seconds"); 
} 
} 
+2

@rozario ogni chiamata di scrittura dovrebbe produrre solo circa 175 MB e quindi cancellarsi.in caso contrario, si finirà con 175 MB x 4 diverse chiamate di scrittura x 5 iterazioni = 3,5 GB di dati. potresti controllare il valore di ritorno da file.delete() e se è falso, lanciare un'eccezione. –

+0

Si noti che 'writer.flush()' non è necessario in questo caso perché 'writer.close()' [svuota la memoria] (http://docs.oracle.com/javase/7/docs/api/java/io /BufferedWriter.html) implicità. BTW: le best practice consigliano di utilizzare [try resource close] (https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) invece di chiamare esplicitamente 'close()'. –

+2

FWIW, questo è stato scritto per Java 5, che almeno non è stato documentato per il flush on close, e che non ha avuto try-with-resources. Potrebbe probabilmente usare l'aggiornamento. –

4

È probabile che la velocità di trasferimento non sia limitata da Java. Invece ho il sospetto (in nessun ordine particolare)

  1. la velocità di trasferimento dal database
  2. la velocità di trasferimento al disco

Se si legge il set di dati completo e poi scrivete fuori su disco, quindi ci vorrà più tempo, poiché la JVM dovrà allocare memoria, e la scrittura db rea/disk avverrà in sequenza. Invece scriverò allo scrittore bufferizzato per ogni lettura che creerai dal db, e quindi l'operazione sarà più vicina a quella concomitante (non so se lo stai facendo o meno)

28

memoria provare i file mappati (prende 300 m/s per scrivere 174MB nel mio m/c, Core 2 Duo, 2,5 GB di RAM):

byte[] buffer = "Help I am trapped in a fortune cookie factory\n".getBytes(); 
int number_of_lines = 400000; 

FileChannel rwChannel = new RandomAccessFile("textfile.txt", "rw").getChannel(); 
ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, buffer.length * number_of_lines); 
for (int i = 0; i < number_of_lines; i++) 
{ 
    wrBuf.put(buffer); 
} 
rwChannel.close(); 
+0

che cosa è aMessage.length() che si intende rappresentare quando stai istanziando ByteBuffer? – Hotel

+2

Jut fyi, eseguendolo su MacBook Pro (fine 2013), 2.6 Ghz Core i7, con Apple 1tb SSD richiede circa 140ms per un 185 megabyte (linee = 4 milioni) – Egwor

+0

qual è il punto del "numero_di_linea"? –

14

Solo per il gusto di statistiche :

La macchina è vecchia Dell con il nuovo SSD

CPU: Intel Pentium D 2,8 Ghz

SSD: Patriot Inferno 120GB SSD

4000000 'records' 
175.47607421875 MB 

Iteration 0 
Writing raw... 3.547 seconds 
Writing buffered (buffer size: 8192)... 2.625 seconds 
Writing buffered (buffer size: 1048576)... 2.203 seconds 
Writing buffered (buffer size: 4194304)... 2.312 seconds 

Iteration 1 
Writing raw... 2.922 seconds 
Writing buffered (buffer size: 8192)... 2.406 seconds 
Writing buffered (buffer size: 1048576)... 2.015 seconds 
Writing buffered (buffer size: 4194304)... 2.282 seconds 

Iteration 2 
Writing raw... 2.828 seconds 
Writing buffered (buffer size: 8192)... 2.109 seconds 
Writing buffered (buffer size: 1048576)... 2.078 seconds 
Writing buffered (buffer size: 4194304)... 2.015 seconds 

Iteration 3 
Writing raw... 3.187 seconds 
Writing buffered (buffer size: 8192)... 2.109 seconds 
Writing buffered (buffer size: 1048576)... 2.094 seconds 
Writing buffered (buffer size: 4194304)... 2.031 seconds 

Iteration 4 
Writing raw... 3.093 seconds 
Writing buffered (buffer size: 8192)... 2.141 seconds 
Writing buffered (buffer size: 1048576)... 2.063 seconds 
Writing buffered (buffer size: 4194304)... 2.016 seconds 

Come si può vedere il metodo grezzo è più lento del buffer.

+0

Tuttavia, il metodo bufferizzato diventa più lento ogni volta che la dimensione del testo è maggiore. – FSm

1

package all.is.well; 
 
import java.io.IOException; 
 
import java.io.RandomAccessFile; 
 
import java.util.concurrent.ExecutorService; 
 
import java.util.concurrent.Executors; 
 
import junit.framework.TestCase; 
 

 
/** 
 
* @author Naresh Bhabat 
 
* 
 
Following implementation helps to deal with extra large files in java. 
 
This program is tested for dealing with 2GB input file. 
 
There are some points where extra logic can be added in future. 
 

 

 
Pleasenote: if we want to deal with binary input file, then instead of reading line,we need to read bytes from read file object. 
 

 

 

 
It uses random access file,which is almost like streaming API. 
 

 

 
* **************************************** 
 
Notes regarding executor framework and its readings. 
 
Please note :ExecutorService executor = Executors.newFixedThreadPool(10); 
 

 
* \t for 10 threads:Total time required for reading and writing the text in 
 
*   :seconds 349.317 
 
* 
 
*   For 100:Total time required for reading the text and writing : seconds 464.042 
 
* 
 
*   For 1000 : Total time required for reading and writing text :466.538 
 
*   For 10000 Total time required for reading and writing in seconds 479.701 
 
* 
 
* 
 
*/ 
 
public class DealWithHugeRecordsinFile extends TestCase { 
 

 
\t static final String FILEPATH = "C:\\springbatch\\bigfile1.txt.txt"; 
 
\t static final String FILEPATH_WRITE = "C:\\springbatch\\writinghere.txt"; 
 
\t static volatile RandomAccessFile fileToWrite; 
 
\t static volatile RandomAccessFile file; 
 
\t static volatile String fileContentsIter; 
 
\t static volatile int position = 0; 
 

 
\t public static void main(String[] args) throws IOException, InterruptedException { 
 
\t \t long currentTimeMillis = System.currentTimeMillis(); 
 

 
\t \t try { 
 
\t \t \t fileToWrite = new RandomAccessFile(FILEPATH_WRITE, "rw");//for random write,independent of thread obstacles 
 
\t \t \t file = new RandomAccessFile(FILEPATH, "r");//for random read,independent of thread obstacles 
 
\t \t \t seriouslyReadProcessAndWriteAsynch(); 
 

 
\t \t } catch (IOException e) { 
 
\t \t \t // TODO Auto-generated catch block 
 
\t \t \t e.printStackTrace(); 
 
\t \t } 
 
\t \t Thread currentThread = Thread.currentThread(); 
 
\t \t System.out.println(currentThread.getName()); 
 
\t \t long currentTimeMillis2 = System.currentTimeMillis(); 
 
\t \t double time_seconds = (currentTimeMillis2 - currentTimeMillis)/1000.0; 
 
\t \t System.out.println("Total time required for reading the text in seconds " + time_seconds); 
 

 
\t } 
 

 
\t /** 
 
\t * @throws IOException 
 
\t * Something asynchronously serious 
 
\t */ 
 
\t public static void seriouslyReadProcessAndWriteAsynch() throws IOException { 
 
\t \t ExecutorService executor = Executors.newFixedThreadPool(10);//pls see for explanation in comments section of the class 
 
\t \t while (true) { 
 
\t \t \t String readLine = file.readLine(); 
 
\t \t \t if (readLine == null) { 
 
\t \t \t \t break; 
 
\t \t \t } 
 
\t \t \t Runnable genuineWorker = new Runnable() { 
 
\t \t \t \t @Override 
 
\t \t \t \t public void run() { 
 
\t \t \t \t \t // do hard processing here in this thread,i have consumed 
 
\t \t \t \t \t // some time and eat some exception in write method. 
 
\t \t \t \t \t writeToFile(FILEPATH_WRITE, readLine); 
 
\t \t \t \t \t // System.out.println(" :" + 
 
\t \t \t \t \t // Thread.currentThread().getName()); 
 

 
\t \t \t \t } 
 
\t \t \t }; 
 
\t \t \t executor.execute(genuineWorker); 
 
\t \t } 
 
\t \t executor.shutdown(); 
 
\t \t while (!executor.isTerminated()) { 
 
\t \t } 
 
\t \t System.out.println("Finished all threads"); 
 
\t \t file.close(); 
 
\t \t fileToWrite.close(); 
 
\t } 
 

 
\t /** 
 
\t * @param filePath 
 
\t * @param data 
 
\t * @param position 
 
\t */ 
 
\t private static void writeToFile(String filePath, String data) { 
 
\t \t try { 
 
\t \t \t // fileToWrite.seek(position); 
 
\t \t \t data = "\n" + data; 
 
\t \t \t if (!data.contains("Randomization")) { 
 
\t \t \t \t return; 
 
\t \t \t } 
 
\t \t \t System.out.println("Let us do something time consuming to make this thread busy"+(position++) + " :" + data); 
 
\t \t \t System.out.println("Lets consume through this loop"); 
 
\t \t \t int i=1000; 
 
\t \t \t while(i>0){ 
 
\t \t \t 
 
\t \t \t \t i--; 
 
\t \t \t } 
 
\t \t \t fileToWrite.write(data.getBytes()); 
 
\t \t \t throw new Exception(); 
 
\t \t } catch (Exception exception) { 
 
\t \t \t System.out.println("exception was thrown but still we are able to proceeed further" 
 
\t \t \t \t \t + " \n This can be used for marking failure of the records"); 
 
\t \t \t //exception.printStackTrace(); 
 

 
\t \t } 
 

 
\t } 
 
}

+0

Aggiungi un testo per spiegare perché questa risposta è migliore di altre risposte. Avere commenti nel codice non è sufficiente. –

+0

La ragione potrebbe essere migliore: è uno scenario in tempo reale ed è in un esempio di stato di lavoro. Altri vantaggi, esso processa la lettura, l'elaborazione e la scrittura in modo asincrono ... Utilizza un file di accesso casuale java api (i.e) che è thread-safe e thread multipli possono leggere e scrivere contemporaneamente. Non causa sovraccarico di memoria in fase di runtime, ma non causa il crash del sistema ... è una soluzione multiuso per gestire il fallimento dell'elaborazione dei record che può essere monitorato nella rispettiva discussione. Per favore fatemi sapere se posso aiutare di più. – RAM

+1

Grazie, queste sono le informazioni necessarie per il tuo post. Forse prendi in considerazione l'aggiunta al corpo del post :) –

Problemi correlati