web-dev-qa-db-fra.com

Le moyen le plus rapide de lire et d'écrire de gros fichiers ligne par ligne en Java

Je cherchais beaucoup le moyen le plus rapide de lire et d'écrire à nouveau des fichiers volumineux (0,5 à 1 Go) en Java avec une mémoire limitée (environ 64 Mo). Chaque ligne du fichier représente un enregistrement, je dois donc les obtenir ligne par ligne. Le fichier est un fichier texte normal.

J'ai essayé BufferedReader et BufferedWriter mais cela ne semble pas être la meilleure option. Il faut environ 35 secondes pour lire et écrire un fichier de taille 0,5 Go, uniquement en lecture-écriture sans traitement. Je pense que le goulot d’étranglement ici est l’écriture car la lecture seule prend environ 10 secondes.

J'ai essayé de lire un tableau d'octets, mais la recherche de lignes dans chaque tableau lu a pris plus de temps.

Des suggestions s'il vous plaît? Merci

18
user1785771

Je soupçonne que votre vrai problème est que votre matériel est limité et que ce que vous faites est qu'un logiciel ne fera pas beaucoup de différence. Si vous avez beaucoup de mémoire et de ressources processeur, des astuces plus avancées peuvent vous aider, mais si vous attendez simplement sur votre disque dur car le fichier n'est pas mis en cache, cela ne fera pas beaucoup de différence.

BTW: 500 Mo en 10 secondes ou 50 Mo/s est une vitesse de lecture typique pour un disque dur.

Essayez d’exécuter ce qui suit pour voir à quel moment votre système ne peut pas mettre le fichier en cache efficacement.

public static void main(String... args) throws IOException {
    for (int mb : new int[]{50, 100, 250, 500, 1000, 2000})
        testFileSize(mb);
}

private static void testFileSize(int mb) throws IOException {
    File file = File.createTempFile("test", ".txt");
    file.deleteOnExit();
    char[] chars = new char[1024];
    Arrays.fill(chars, 'A');
    String longLine = new String(chars);
    long start1 = System.nanoTime();
    PrintWriter pw = new PrintWriter(new FileWriter(file));
    for (int i = 0; i < mb * 1024; i++)
        pw.println(longLine);
    pw.close();
    long time1 = System.nanoTime() - start1;
    System.out.printf("Took %.3f seconds to write to a %d MB, file rate: %.1f MB/s%n",
            time1 / 1e9, file.length() >> 20, file.length() * 1000.0 / time1);

    long start2 = System.nanoTime();
    BufferedReader br = new BufferedReader(new FileReader(file));
    for (String line; (line = br.readLine()) != null; ) {
    }
    br.close();
    long time2 = System.nanoTime() - start2;
    System.out.printf("Took %.3f seconds to read to a %d MB file, rate: %.1f MB/s%n",
            time2 / 1e9, file.length() >> 20, file.length() * 1000.0 / time2);
    file.delete();
}

Sur une machine Linux avec beaucoup de mémoire.

Took 0.395 seconds to write to a 50 MB, file rate: 133.0 MB/s
Took 0.375 seconds to read to a 50 MB file, rate: 140.0 MB/s
Took 0.669 seconds to write to a 100 MB, file rate: 156.9 MB/s
Took 0.569 seconds to read to a 100 MB file, rate: 184.6 MB/s
Took 1.585 seconds to write to a 250 MB, file rate: 165.5 MB/s
Took 1.274 seconds to read to a 250 MB file, rate: 206.0 MB/s
Took 2.513 seconds to write to a 500 MB, file rate: 208.8 MB/s
Took 2.332 seconds to read to a 500 MB file, rate: 225.1 MB/s
Took 5.094 seconds to write to a 1000 MB, file rate: 206.0 MB/s
Took 5.041 seconds to read to a 1000 MB file, rate: 208.2 MB/s
Took 11.509 seconds to write to a 2001 MB, file rate: 182.4 MB/s
Took 9.681 seconds to read to a 2001 MB file, rate: 216.8 MB/s

Sur une machine Windows avec beaucoup de mémoire.

Took 0.376 seconds to write to a 50 MB, file rate: 139.7 MB/s
Took 0.401 seconds to read to a 50 MB file, rate: 131.1 MB/s
Took 0.517 seconds to write to a 100 MB, file rate: 203.1 MB/s
Took 0.520 seconds to read to a 100 MB file, rate: 201.9 MB/s
Took 1.344 seconds to write to a 250 MB, file rate: 195.4 MB/s
Took 1.387 seconds to read to a 250 MB file, rate: 189.4 MB/s
Took 2.368 seconds to write to a 500 MB, file rate: 221.8 MB/s
Took 2.454 seconds to read to a 500 MB file, rate: 214.1 MB/s
Took 4.985 seconds to write to a 1001 MB, file rate: 210.7 MB/s
Took 5.132 seconds to read to a 1001 MB file, rate: 204.7 MB/s
Took 10.276 seconds to write to a 2003 MB, file rate: 204.5 MB/s
Took 9.964 seconds to read to a 2003 MB file, rate: 210.9 MB/s
15
Peter Lawrey

La première chose que je voudrais essayer est d’augmenter la taille de la mémoire tampon de BufferedReader et BufferedWriter. Les tailles de tampon par défaut ne sont pas documentées, mais au moins dans Oracle VM, elles ont 8192 caractères, ce qui n'apportera pas un avantage considérable en termes de performances. 

Si vous avez seulement besoin de faire une copie du fichier (et que vous n'avez pas besoin d'accéder aux données), je vais soit abandonner l'approche Lecteur/Écrivain et travailler directement avec InputStream et OutputStream en utilisant un tableau d'octets comme tampon:

FileInputStream fis = new FileInputStream("d:/test.txt");
FileOutputStream fos = new FileOutputStream("d:/test2.txt");
byte[] b = new byte[bufferSize];
int r;
while ((r=fis.read(b))>=0) {
    fos.write(b, 0, r);         
}
fis.close();
fos.close();

ou utilisez réellement NIO:

FileChannel in = new RandomAccessFile("d:/test.txt", "r").getChannel();
FileChannel out = new RandomAccessFile("d:/test2.txt", "rw").getChannel();
out.transferFrom(in, 0, Long.MAX_VALUE);
in.close();
out.close();

Lors de l'analyse comparative des différentes méthodes de copie, les différences (durée) entre chaque exécution du test de performance sont toutefois beaucoup plus grandes que celles entre les différentes implémentations. La mise en cache d'E/S (à la fois au niveau du système d'exploitation et de la mémoire cache du disque dur) joue ici un rôle important et il est très difficile de dire ce qui est plus rapide. Sur mon matériel, copier un fichier texte de 1 Go ligne par ligne à l'aide de BufferedReader et BufferedWriter prend moins de 5 secondes dans certaines exécutions et plus de 30 secondes dans d'autres.

9
jarnbjo

Dans Java 7, vous pouvez utiliser les méthodes Files.readAllLines () et Files.write (). Voici l'exemple:

List<String> readTextFile(String fileName) throws IOException {
    Path path = Paths.get(fileName);
    return Files.readAllLines(path, StandardCharsets.UTF_8);
}

void writeTextFile(List<String> strLines, String fileName) throws IOException {
    Path path = Paths.get(fileName);
    Files.write(path, strLines, StandardCharsets.UTF_8);
}
4
Oleg K

Je recommanderais de regarder les classes dans le package Java.nio. IO non bloquant pourrait être plus rapide pour les sockets:

http://docs.Oracle.com/javase/6/docs/api/Java/nio/package-summary.html

Cet article a des points de repère qui disent que c'est vrai:

http://vanillajava.blogspot.com/2010/07/Java-nio-is-faster-than-Java-io-for.html

2
duffymo

J'ai écrit un article complet sur les nombreuses façons de lire des fichiers en Java et de les tester les uns contre les autres avec des exemples de fichiers de 1 Ko à 1 Go. J'ai trouvé que les 3 méthodes suivantes étaient les plus rapides pour lire des fichiers de 1 Go. :

1) Java.nio.file.Files.readAllBytes () - a pris un peu moins d’une seconde pour lire un fichier de test de 1 Go.

import Java.io.File;
import Java.io.IOException;
import Java.nio.file.Files;

public class ReadFile_Files_ReadAllBytes {
  public static void main(String [] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    File file = new File(fileName);

    byte [] fileBytes = Files.readAllBytes(file.toPath());
    char singleChar;
    for(byte b : fileBytes) {
      singleChar = (char) b;
      System.out.print(singleChar);
    }
  }
}

2) Java.nio.file.Files.lines () - Il a fallu environ 3,5 secondes pour lire un fichier de test de 1 Go.

import Java.io.File;
import Java.io.IOException;
import Java.nio.file.Files;
import Java.util.stream.Stream;

public class ReadFile_Files_Lines {
  public static void main(String[] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    File file = new File(fileName);

    try (Stream linesStream = Files.lines(file.toPath())) {
      linesStream.forEach(line -&gt; {
        System.out.println(line);
      });
    }
  }
}

3) Java.io.BufferedReader - Il a fallu environ 4,5 secondes pour lire un fichier de test de 1 Go.

import Java.io.BufferedReader;
import Java.io.FileReader;
import Java.io.IOException;

public class ReadFile_BufferedReader_ReadLine {
  public static void main(String [] args) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    FileReader fileReader = new FileReader(fileName);

    try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
      String line;
      while((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
      }
    }
  }
}
0
gomisha