web-dev-qa-db-fra.com

Pourquoi les performances de BufferedReader sont-elles tellement pires que BufferedInputStream?

Je comprends que l’utilisation d’un BufferedReader (encapsuler un FileReader) sera beaucoup plus lente que celle d’un BufferedInputStream (encapsuler un FileInputStream), car les octets bruts doivent être convertis en caractères. Mais je ne comprends pas pourquoi c'est tellement plus lent! Voici les deux exemples de code que j'utilise:

BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(filename));
try {
  byte[] byteBuffer = new byte[bufferSize];
  int numberOfBytes;
  do {
    numberOfBytes = inputStream.read(byteBuffer, 0, bufferSize);
  } while (numberOfBytes >= 0);
}
finally {
  inputStream.close();
}

et:

BufferedReader reader = new BufferedReader(new FileReader(filename), bufferSize);
try {
  char[] charBuffer = new char[bufferSize];
  int numberOfChars;
  do {
    numberOfChars = reader.read(charBuffer, 0, bufferSize);
  } while (numberOfChars >= 0);
}
finally {
  reader.close();
}

J'ai essayé des tests utilisant différentes tailles de mémoire tampon, le tout avec un fichier de 150 Mo. Voici les résultats (la taille de la mémoire tampon est en octets; les temps sont en millisecondes):

Buffer   Input
  Size  Stream  Reader
 4,096    145     497
 8,192    125     465
16,384     95     515
32,768     74     506
65,536     64     531

Comme on peut le constater, le temps le plus rapide pour BufferedInputStream (64 ms) est sept fois plus rapide que le temps le plus rapide pour BufferedReader (465 ms). Comme je l’ai dit plus haut, je n’ai pas de problème avec une différence significative; mais cette différence semble tout simplement déraisonnable.

Ma question est la suivante: quelqu'un a-t-il une suggestion pour améliorer les performances de BufferedReader ou un mécanisme alternatif?

16
Andy King

BufferedReader a converti les octets en caractères. Cet octet, octet, l'analyse syntaxique et la copie dans un type plus volumineux coûte cher par rapport à une copie simple de blocs de données.

byte[] bytes = new byte[150 * 1024 * 1024];
Arrays.fill(bytes, (byte) '\n');

for (int i = 0; i < 10; i++) {
    long start = System.nanoTime();
    StandardCharsets.UTF_8.decode(ByteBuffer.wrap(bytes));
    long time = System.nanoTime() - start;
    System.out.printf("Time to decode %,d MB was %,d ms%n",
            bytes.length / 1024 / 1024, time / 1000000);
}

empreintes

Time to decode 150 MB was 226 ms
Time to decode 150 MB was 167 ms

REMARQUE: cette opération mélangée à des appels système peut ralentir les deux opérations (car les appels système peuvent perturber le cache).

14
Peter Lawrey

dans l'implémentation BufferedReader, il existe une constante fixe defaultExpectedLineLength = 80, qui est utilisée dans la méthode readLine lors de l'allocation de StringBuffer. Si vous avez un gros fichier avec beaucoup de lignes plus longues que 80, ce fragment peut être quelque chose qui peut être amélioré

if (s == null) 
    s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
2
Jakub C