为什么这个 "line count" 程序在 Java 中变慢了?使用 MappedByteBuffer
Why is this "line count" program slow in Java? Using MappedByteBuffer
为了尝试MappedByteBuffer
(Java中的内存映射文件),我写了一个简单的wc -l
(文本文件行数)演示:
int wordCount(String fileName) throws IOException {
FileChannel fc = new RandomAccessFile(new File(fileName), "r").getChannel();
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
int nlines = 0;
byte newline = '\n';
for(long i = 0; i < fc.size(); i++) {
if(mem.get() == newline)
nlines += 1;
}
return nlines;
}
我在大约 15 MB(15008641 字节)和 100k 行的文件上试过这个。在我的笔记本电脑上,大约需要 13.8 sec
。为什么这么慢?
完整的 class 代码在这里:http://pastebin.com/t8PLRGMa
为了参考,我用C写了同样的想法:http://pastebin.com/hXnDvZm6
它运行大约 28 毫秒,或 490 times faster
。
出于好奇,我还使用与 Java 中基本相同的算法和 API 编写了一个 Scala 版本。它运行 10 times faster
,这表明肯定发生了一些奇怪的事情。
更新:文件由OS缓存,因此不涉及磁盘加载时间。
我想使用内存映射来随机访问可能不适合 RAM 的更大文件。这就是我不只是使用 BufferedReader 的原因。
代码很慢,因为在循环中调用了fc.size()
。
JVM 显然无法消除 fc.size()
,因为文件大小可以在 运行 时间内更改。查询文件大小比较慢,因为需要对底层文件系统进行系统调用。
将此更改为
long size = fc.size();
for (long i = 0; i < size; i++) {
...
}
为了尝试MappedByteBuffer
(Java中的内存映射文件),我写了一个简单的wc -l
(文本文件行数)演示:
int wordCount(String fileName) throws IOException {
FileChannel fc = new RandomAccessFile(new File(fileName), "r").getChannel();
MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
int nlines = 0;
byte newline = '\n';
for(long i = 0; i < fc.size(); i++) {
if(mem.get() == newline)
nlines += 1;
}
return nlines;
}
我在大约 15 MB(15008641 字节)和 100k 行的文件上试过这个。在我的笔记本电脑上,大约需要 13.8 sec
。为什么这么慢?
完整的 class 代码在这里:http://pastebin.com/t8PLRGMa
为了参考,我用C写了同样的想法:http://pastebin.com/hXnDvZm6
它运行大约 28 毫秒,或 490 times faster
。
出于好奇,我还使用与 Java 中基本相同的算法和 API 编写了一个 Scala 版本。它运行 10 times faster
,这表明肯定发生了一些奇怪的事情。
更新:文件由OS缓存,因此不涉及磁盘加载时间。
我想使用内存映射来随机访问可能不适合 RAM 的更大文件。这就是我不只是使用 BufferedReader 的原因。
代码很慢,因为在循环中调用了fc.size()
。
JVM 显然无法消除 fc.size()
,因为文件大小可以在 运行 时间内更改。查询文件大小比较慢,因为需要对底层文件系统进行系统调用。
将此更改为
long size = fc.size();
for (long i = 0; i < size; i++) {
...
}