Ruby: начать чтение с произвольной точки в большом файле

У меня есть несколько файлов журнала, которые я хотел бы просмотреть. Содержание именно то, что вы ожидаете в файле журнала: много строк текста, разделенных запятыми. Файлы около 4 гигов каждый. File.each_line или foreach занимает около 20 минут для одного из них.

Поскольку простой foreach кажется... простым (и медленным), я подумал, что два отдельных потока могут работать с одним файлом, если я скажу им только с чего начать. Но, основываясь на моих (ограниченных) знаниях, я не могу решить, возможно ли это вообще.

Есть ли способ начать чтение файла с произвольной строки?

4 ответа

Решение

Для строк это может быть немного сложно, но вы можете искать в файле определенный байт.

IO#seek (ссылка) и IO#pos (ссылка) позволит вам искать определенный байт в файле.

Чтобы увидеть разницу между всеми файлами сразу и построчно, я проверил файл размером около 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

Другие вопросы по тегам