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