Ruby: начать чтение с произвольной точки в большом файле
У меня есть несколько файлов журнала, которые я хотел бы просмотреть. Содержание именно то, что вы ожидаете в файле журнала: много строк текста, разделенных запятыми. Файлы около 4 гигов каждый. File.each_line или foreach занимает около 20 минут для одного из них.
Поскольку простой foreach кажется... простым (и медленным), я подумал, что два отдельных потока могут работать с одним файлом, если я скажу им только с чего начать. Но, основываясь на моих (ограниченных) знаниях, я не могу решить, возможно ли это вообще.
Есть ли способ начать чтение файла с произвольной строки?
4 ответа
Чтобы увидеть разницу между всеми файлами сразу и построчно, я проверил файл размером около 99 МБ с более чем 1 000 000 строк.
greg-mbp-wireless:Desktop greg$ wc filelist.txt
1003002 1657573 99392863 filelist.txt
Я поместил следующий цикл в файл ruby и запустил его из командной строки командой time:
IO.read(ARGV.first).lines { |l|
}
greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt
real 0m1.411s
user 0m0.653s
sys 0m0.169s
Затем я изменил его, чтобы читать построчно, и рассчитал это тоже:
IO.readlines(ARGV.first) { |l|
}
greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt
real 0m1.053s
user 0m0.741s
sys 0m0.278s
Я не уверен, почему, но чтение построчно быстрее. Это может быть связано с распределением памяти, так как Ruby пытается загрузить весь файл в ОЗУ в первом примере, или, возможно, это была аномалия, так как я делал тест только один раз для каждого файла. Используя read
с явным размером файла может быть быстрее, так как Ruby будет знать, сколько ему нужно выделить заранее.
И это было все, что мне нужно, чтобы проверить это:
fcontent = ''
File.open(ARGV.first, 'r') do |fi|
fsize = fi.size
fcontent = fi.read(fsize)
end
puts fcontent.size
greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt
99392863
real 0m0.168s
user 0m0.010s
sys 0m0.156s
Похоже, знание того, сколько нужно прочитать, имеет большое значение.
Добавление обратно в цикл над строковым буфером приводит к этому:
File.open(ARGV.first, 'r') do |fi|
fsize = fi.size
fi.read(fsize).lines { |l|
}
end
greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt
real 0m0.732s
user 0m0.572s
sys 0m0.158s
Это все еще улучшение.
Если вы использовали очередь и передавали ее из потока, который отвечал за чтение файла, а затем использовали очередь из того, что обрабатывает входящий текст, тогда вы можете увидеть более высокую общую пропускную способность.
Если вы хотите начать с определенной строки в файле, я бы порекомендовал просто убрать хвост.
excerpt = `tail -m +5000 filename.log`
Это даст вам содержимое файла filename.log от строки 5000 до конца файла.
Попробуйте quick_csv, если вы этого еще не сделали, и если это все еще слишком медленно, используйте что-то, имеющее собственные расширения в c, как это - http://github.com/wwood/excelsior