Ruby:开始在大文件中的任意点读取
我有一些我希望筛选的日志文件。 内容正是您在日志文件中所期望的:许多单行的逗号分隔文本。 这些文件大约每个4演出。 其中一个File.each_line或foreach大约需要20分钟。
由于简单的foreach似乎……简单(而且速度慢),我认为如果我只能告诉他们从哪里开始,两个单独的线程可能能够在同一个文件上工作。 但基于我(有限的)知识,我无法决定这是否可能。
有没有办法开始在任意行读取文件?
对于行,它可能有点困难,但您可以在文件中寻找某个字节。
IO#seek
(链接)和IO#pos
(链接)都允许您搜索文件中的给定字节。
为了看看整个文件一次又一次地诋毁整个文件有什么区别,我测试了一个大约99MB的文件,超过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在第一个示例中尝试将整个文件加载到RAM中,或者可能是exception,因为我只为每个文件执行了一次测试。 使用带有显式文件大小的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
这仍然是一个进步。
如果您使用Queue并从负责读取文件的线程中提供它,那么从传入文本的任何进程中消耗队列,那么您可能会看到更高的总吞吐量。
如果你想从文件中的特定行开始,我建议你只需要尾随。
excerpt = `tail -m +5000 filename.log`
这将为您提供从第5000行到文件末尾的filename.log的内容。
尝试使用faster_csv,如果你还没有,如果那仍然太慢,那么在c中使用具有原生扩展名的东西 – http://github.com/wwood/excelsior