2011-01-01 22 views
27

Hice un método que toma un File y un String. Reemplaza el archivo con un nuevo archivo con esa cadena como su contenido.¿La forma más rápida de escribir en un archivo?

Esto es lo que hice:

public static void Save(File file, String textToSave) { 

    file.delete(); 
    try { 
     BufferedWriter out = new BufferedWriter(new FileWriter(file)); 
     out.write(textToSave); 
     out.close(); 
    } catch (IOException e) { 
    } 
} 

Sin embargo, es muy lento. A veces lleva más de un minuto.

¿Cómo puedo escribir archivos de gran tamaño con decenas de miles hasta tal vez hasta un millón de caracteres en ellos?

+8

No es necesario borrar el archivo. Lo estás sobreescribiendo. –

+1

¿Cuánto tiempo es el tiempo de CPU y cuánto tiempo de E/S ("sistema")? Para archivos grandes, la enorme cadena 'textToSave' puede dominar el tiempo. – Raedwald

+3

No relacionado directamente con su pregunta: Puede considerar reestructurar la sentencia out.close() para que se pueda hacer en un bloque finally. En caso de que se genere un error al escribir, aún se cerraría. –

Respuesta

14

Asegúrese de asignar una gran amortiguación suficiente:

BufferedWriter out = new BufferedWriter(new FileWriter(file), 32768); 

¿Qué tipo de sistema operativo que está ejecutando en? Eso también puede marcar una gran diferencia. Sin embargo, tomar un minuto para escribir un archivo de tamaño inferior a un sonido enorme como un problema del sistema. En Linux u otros sistemas * ix, puede usar cosas como strace para ver si la JVM realiza muchas llamadas al sistema innecesarias. (Hace mucho tiempo, Java I/O era bastante tonto y haría locas llamadas a sistemas de bajo nivel write() si no tenías cuidado, pero cuando digo "hace mucho tiempo" me refiero a 1998 más o menos).

— editar nota que la situación de un programa Java escribir un simple archivo de una manera sencilla, y sin embargo ser muy lento, es uno inherentemente extraño. ¿Puedes decir si la CPU está muy cargada mientras se está escribiendo el archivo? No debería ser; casi no debería haber carga de CPU de tal cosa.

+0

De acuerdo. Incluso podría saber el tamaño del búfer necesario por adelantado, ya que está tomando el String como param: textToSave.getBytes(). Longitud –

+0

@Rocky Madden sí, ese es un buen punto. Sin embargo, volcar una cadena a través de las bibliotecas de Java IO debería ser bastante rápido casi de cualquier forma que lo haga. – Pointy

+0

getBytes() puede ser muy costoso solo para ajustar un buffer. Te sugiero que solo hagas 256K y no te preocupes por eso. –

-3

En Java, el BufferWriter es muy lento: utilice los métodos nativos directamente, y llámelos lo menos posible (proporcióneles la mayor cantidad de datos por llamada que pueda).

try{ 
     FileOutputStream file=new FileOutputStream(file); 
     file.write(content); 
     file.close(); 
    }catch(Throwable e){ 
     D.error(e); 
    }//try 

Además, la eliminación del archivo puede tomar un tiempo (tal vez está siendo copiado a la papelera de reciclaje en primer lugar). Simplemente sobrescriba el archivo, como en el código anterior.

+0

No he tenido experiencia con BufferedWriter siendo "muy lento" en absoluto, y he estado escribiendo código Java del lado del servidor durante mucho tiempo. No creo que sea lo que usaría si tuviera una aplicación de megaproducción muy seria, tal vez, pero no es tan mala; ¿como puede ser? – Pointy

+1

del mismo modo, nunca he visto una llamada a File # delete() mover un archivo a una papelera de reciclaje. Eliminar significa eliminar. –

+0

Puntiagudo: Sí, probablemente fue "hace mucho tiempo" que rastreé las escrituras del archivo Java a través del depurador MS para ver la cantidad inane de llamadas al sistema que estaba haciendo en mi máquina. –

13

Una prueba simple para que usted

char[] chars = new char[100*1024*1024]; 
Arrays.fill(chars, 'A'); 
String text = new String(chars); 
long start = System.nanoTime(); 
BufferedWriter bw = new BufferedWriter(new FileWriter("/tmp/a.txt")); 
bw.write(text); 
bw.close(); 
long time = System.nanoTime() - start; 
System.out.println("Wrote " + chars.length*1000L/time+" MB/s."); 

imprime

Wrote 135 MB/s. 
3

Intente utilizar archivos de memoria mapeada:

FileChannel rwChannel = new RandomAccessFile("textfile.txt", "rw").getChannel(); 
ByteBuffer wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, textToSave.length()); 

wrBuf.put(textToSave.getBytes()); 

rwChannel.close(); 
0

Hola He creado dos enfoques para crear archivos de gran tamaño, los programas de gestión en Windows 7, 64 bits, 8 GB de RAM, JDK 8 y siguientes son resultados.
En ambos casos, se creó un archivo de 180 MB que contiene un número en cada línea de 1 a 20 millones (2 millones de rupias en el sistema de la India).

memoria de programa de Java crece gradualmente hasta 600 MB

primera salida

Approach = approach-1 (Using FileWriter) 
Completed file writing in milli seconds = 4521 milli seconds. 

Salida segundo

Approach = approach-2 (Using FileChannel and ByteBuffer) 
Completed file writing in milli seconds = 3590 milli seconds. 

Una observación - Estoy posición (variable POS) calcular en el enfoque # 2, si lo comento, solo la última cuerda será visible debido a que se sobreescribirá en la posición, pero el tiempo se reducirá a casi 2000 milisegundos.

Código adjunto.

import java.io.FileWriter; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.nio.ByteBuffer; 
import java.nio.channels.FileChannel; 
import java.util.concurrent.TimeUnit; 

public class TestLargeFile { 

    public static void main(String[] args) { 
     writeBigFile(); 
    } 

    private static void writeBigFile() { 
     System.out.println("--------writeBigFile-----------"); 
     long nanoTime = System.nanoTime(); 
     String fn = "big-file.txt"; 
     boolean approach1 = false; 
     System.out.println("Approach = " + (approach1 ? "approach-1" : "approach-2")); 
     int numLines = 20_000_000; 
     try { 
      if (approach1) { 
       //Approach 1 -- for 2 crore lines takes 4.5 seconds with 180 mb file size 
       approach1(fn, numLines); 
      } else { 
       //Approach 2 -- for 2 crore lines takes nearly 2 to 2.5 seconds with 180 mb file size 
       approach2(fn, numLines); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     System.out.println("Completed file writing in milli seconds = " + TimeUnit.MILLISECONDS.convert((System.nanoTime() - nanoTime), TimeUnit.NANOSECONDS)); 
    } 

    private static void approach2(String fn, int numLines) throws IOException { 
     StringBuilder sb = new StringBuilder(); 
     FileChannel rwChannel = new RandomAccessFile(fn, "rw").getChannel(); 
     ByteBuffer wrBuf; 

     int pos = 0; 
     for (int i = 1; i <= numLines; i++) { 
      sb.append(i).append(System.lineSeparator()); 
      if (i % 100000 == 0) { 
       wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length()); 
       pos += sb.length(); 
       wrBuf.put(sb.toString().getBytes()); 
       sb = new StringBuilder(); 
      } 
     } 
     if (sb.length() > 0) { 
      wrBuf = rwChannel.map(FileChannel.MapMode.READ_WRITE, pos, sb.length()); 
      wrBuf.put(sb.toString().getBytes()); 
     } 
     rwChannel.close(); 
    } 

    private static void approach1(String fn, int numLines) throws IOException { 
     StringBuilder sb = new StringBuilder(); 
     for (int i = 1; i <= numLines; i++) { 
      sb.append(i).append(System.lineSeparator()); 
     } 
     FileWriter fileWriter = new FileWriter(fn); 
     fileWriter.write(sb.toString()); 
     fileWriter.flush(); 
     fileWriter.close(); 
    } 
} 
Cuestiones relacionadas