web-dev-qa-db-ja.com

BufferedReaderのパフォーマンスがBufferedInputStreamよりもはるかに悪いのはなぜですか?

生のバイトを文字に変換する必要があるため、BufferedReaderの使用(FileReaderのラップ)はBufferedInputStreamの使用(FileInputStreamのラップ)よりも大幅に遅くなることを理解しています。しかし、なぜそんなに遅いのかわかりません!これが私が使用している2つのコードサンプルです:

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();
}

そして:

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();
}

さまざまなバッファサイズを使用して、すべて150メガバイトのファイルでテストを試しました。結果は次のとおりです(バッファサイズはバイト単位、時間はミリ秒単位)。

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

ご覧のとおり、BufferedInputStreamの最速時間(64ミリ秒)は、BufferedReaderの最速時間(465ミリ秒)よりも7倍高速です。上で述べたように、大きな違いはありません。しかし、これだけの違いは不合理に思えます。

私の質問は:BufferedReaderのパフォーマンスを改善する方法、または代替メカニズムについて誰かが提案を持っていますか?

17
Andy King

BufferedReaderはバイトを文字に変換しました。このバイトごとの解析とより大きなタイプへのコピーは、データブロックのストレートコピーに比べてコストがかかります。

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

プリント

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

注:これをシステムコールと組み合わせて実行する必要があると、両方の操作が遅くなる可能性があります(システムコールがキャッシュを妨害する可能性があるため)

15
Peter Lawrey

bufferedReaderの実装には、固定定数defaultExpectedLineLength = 80があります。これは、readLineを割り当てるときにStringBufferメソッドで使用されます。行数が80を超える大きなファイルがある場合、このフラグメントは改善できる可能性があります

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