Почему эта программа "подсчета строк" работает медленно в Java? Использование MappedByteBuffer
Пытаться MappedByteBuffer
(файл отображен в памяти на Java), я написал простой wc -l
(количество строк в текстовом файле) demo:
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 МБ (15008641 байт) и 100 тыс. Строк. На моем ноутбуке это занимает около 13.8 sec
, Почему это так медленно?
Полный код класса здесь: http://pastebin.com/t8PLRGMa
Для справки я написал ту же идею в C: http://pastebin.com/hXnDvZm6
Это работает около 28 мс, или 490 times faster
,
Из любопытства я также написал версию Scala, использующую, по сути, тот же алгоритм и API, что и в Java. Работает 10 times faster
, который предполагает, что определенно происходит что-то странное.
Обновление: файл кэшируется операционной системой, поэтому время загрузки диска не требуется.
Я хотел использовать отображение памяти для произвольного доступа к большим файлам, которые могут не помещаться в ОЗУ. Вот почему я не просто использую BufferedReader.
1 ответ
Код очень медленный, потому что fc.size()
вызывается в цикле.
JVM явно не может устранить fc.size()
, так как размер файла может быть изменен во время выполнения. Запрос размера файла является относительно медленным, потому что он требует системного вызова базовой файловой системы.
Изменить это на
long size = fc.size();
for (long i = 0; i < size; i++) {
...
}