2011-01-01 14 views
27

Zrobiłem metodę, która ma File i String. Zastępuje plik nowym plikiem z tym ciągiem jako jego treścią.Najszybszy sposób zapisu do pliku?

To co zrobiłem:

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) { 
    } 
} 

Jednak jest boleśnie powolny. Czasami zajmuje to ponad minutę.

Jak pisać duże pliki o dziesiątkach tysięcy, a może nawet milionach znaków?

+8

Usuwanie pliku nie jest konieczne. Nadpisujesz to. –

+1

Ile czasu zajmuje procesor i ile czasu we/wy ("system")? W przypadku dużych plików tworzenie ogromnego ciągu 'textToSave' może zdominować czas. – Raedwald

+3

Nie dotyczy bezpośrednio Twojego pytania: Możesz rozważyć zmianę instrukcji out.close(), aby można było to zrobić w bloku finally. W przypadku wystąpienia błędu przy zapisie, będzie on nadal zamknięty. –

Odpowiedz

14

Upewnij się przeznaczyć wystarczająco duży bufor:

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

Jakie OS są uruchomione na? To też może zrobić dużą różnicę. Jednak pobranie minutę w celu zapisania pliku o rozmiarze mniejszym od ogromnego brzmi jak problem z systemem. W systemach Linux i innych systemach * ix można używać rzeczy takich jak strace, aby sprawdzić, czy JVM wykonuje wiele niepotrzebnych wywołań systemowych. (Bardzo wcześnie, Java I/O było dość głupie i tworzyłoby szalone liczby niskopoziomowych wywołań systemowych write(), gdybyś nie był ostrożny, ale kiedy mówię "dawno temu" mam na myśli rok 1998 lub coś koło tego.)

edit — uwaga, że ​​sytuacja programu Java napisanie prostego pliku w sposób prosty, a jednocześnie jest bardzo powolny, jest z natury dziwne jeden. Czy możesz stwierdzić, czy procesor jest mocno obciążony podczas zapisywania pliku? Nie powinno tak być; nie powinno być prawie żadnego obciążenia procesora z takiej rzeczy.

+0

Uzgodnione. Może nawet będzie w stanie poznać rozmiar bufora potrzebny z góry, ponieważ bierze ciąg jako parametr: textToSave.getBytes(). Length –

+0

@Rocky Madden yea to naprawdę dobry punkt. Jednak zrzucanie łańcucha znaków przez biblioteki Java IO powinno być dość szybkie prawie w dowolny sposób. – Pointy

+0

getBytes() może być bardzo kosztowne, tylko dostroić bufor. Proponuję po prostu zrobić to 256K i nie martwić się o to. –

-3

W Javie, BufferWriter jest bardzo powolny: Użyj natywnych metod bezpośrednio i wywołaj je tak mało, jak to tylko możliwe (podaj jak najwięcej danych na połączenie, jak możesz).

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

Usunięcie pliku może trochę potrwać (może najpierw zostanie skopiowane do kosza). Wystarczy nadpisać plik, jak w powyższym kodzie.

+0

Nie miałem doświadczenia z tym, że BufferedWriter jest "bardzo wolny", a ja od dawna pisałem kod Java po stronie serwera. Nie sądzę, żeby to było to, czego bym użył, gdybym miał jakąś bardzo poważną aplikację megaprzepustową, ale nie jest tak źle; jak to mogło się stać? – Pointy

+1

podobnie jak nigdy nie widziałem połączenia z File # delete() przenieś plik do kosza. Usuń oznacza usunięcie. –

+0

Pointy: Tak, prawdopodobnie "dawno temu" śledziłem zapisywanie plików Java za pomocą debugera MS, aby zobaczyć nieudaną liczbę wywołań systemowych wykonywanych na moim komputerze. –

13

Prosty test dla ciebie

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."); 

Drukuje

Wrote 135 MB/s. 
3

Spróbuj użyć pamięci mapowane pliki:

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

Witam mam utworzone dwa podejścia do tworzenia dużych plików, uruchomić programu na Windows 7, 64-bit, 8 GB RAM maszyny, JDK 8 i poniżej są wyniki.
W obu przypadkach utworzono plik 180 MB zawierający liczbę w każdej linii od 1 do 20 milionów (2 crore w systemie indyjskim).

pamięć programu Java rośnie stopniowo till 600 MB

pierwsze wyjście

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

drugie wyjście

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

Jedno spostrzeżenie - Jestem obliczania pozycji (zmienny pos) podejścia nr 2, jeśli skomentuję, to tylko ostatni ciąg będzie widoczny z powodu nadpisania na pozycji, ale czas zmniejszony do prawie 2000 milli sekund.

Dołączanie kodu.

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(); 
    } 
} 
Powiązane problemy