19

我知道使用 BufferedReader(包装 FileReader)会比使用 BufferedInputStream(包装 FileInputStream)慢得多,因为必须将原始字节转换为字符。但我不明白为什么它这么慢!这是我正在使用的两个代码示例:

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 毫秒)快七倍。正如我上面所说,我没有显着差异的问题。但是这么大的差异似乎是不合理的。

我的问题是:是否有人对如何提高 BufferedReader 或替代机制的性能提出建议?

4

2 回答 2

15

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

注意:必须与系统调用混合执行此操作会减慢这两种操作(因为系统调用会干扰缓存)

于 2013-01-13T09:56:15.357 回答
3

在 BufferedReader 实现中有一个固定常量defaultExpectedLineLength = 80readLine在分配时在方法中使用StringBuffer。如果你有很多行超过 80 行的大文件,这个片段可能是可以改进的

if (s == null) 
    s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
于 2014-10-21T08:23:45.890 回答