Ruby i / o性能 - 通过char读取文件char

时间:2016-12-10 11:34:18

标签: ruby performance io

简短版本 如何使用Ruby在保持高性能的同时从char读取STDIN(或文件)char? (虽然问题可能不是特定于Ruby的)

长版: 在学习Ruby的同时,我设计了一个必须从管道文本数据中读取的小实用程序,在其中查找并收集数字并进行一些处理。

cat huge_text_file.txt | program.rb

input  > 123123sdas234sdsd5a ...
output > 123123, 234, 5, ...

文本输入可能很大(千兆字节),它可能不包含换行符或空格(任何非数字字符都是分隔符)所以我通过字符阅读做了一个字符(尽管我对性能有所顾虑)并且它结果这样做非常慢。

只需通过char读取char而不处理900Kb输入文件大约需要7秒钟!

while c = STDIN.read(1)
end

如果我用换行符输入数据并逐行读取,则读取同一文件的速度要快100倍。

while s = STDIN.gets
end

似乎从STDIN.read(1)的管道中读取并不涉及任何缓冲,每次读取都会发生硬盘驱动器 - 但不应该被操作系统缓存吗?

没有STDIN.gets在内部使用char读取char,直到遇到' \n'?

使用C,我可能会以块的形式读取数据,但我不得不处理由缓冲区窗口分割的数字,但这看起来不像Ruby的优雅解决方案。那么这样做的正确方法是什么?

P.S Timing在Python中读取同一个文件:

for line in f:
    line
f.close()

运行时间为0.01秒。

c = f.read(1)
while c:
    c = f.read(1)
f.close()

运行时间为0.17秒。

谢谢!

1 个答案:

答案 0 :(得分:3)

此脚本逐字读取IO对象,并在每次找到1000个单词或到达文件末尾时执行该块。

同时在内存中保存不超过1000个单词。请注意,使用" "作为分隔符意味着“字”可能包含换行符。

此脚本使用IO#each指定分隔符(在本例中为空格,以获取Enumerator个单词),lazy以避免对整个文件内容执行任何操作, each_slice获取一系列batch_size单词。

batch_size = 1000

STDIN.each(" ").lazy.each_slice(batch_size) do |batch|
  # batch is an Array of batch_size words
end

您也可以直接阅读该文件,而不是使用cat和|

batch_size = 1000

File.open('huge_text_file.txt').each(" ").lazy.each_slice(batch_size) do |batch|
  # batch is an Array of batch_size words
end

使用此代码,不会分割任何数字,不需要逻辑,它应该比通过char读取文件char快得多,并且它将比将整个文件读入String中使用更少的内存。