Ruby i/o 性能 - 按字符读取文件

eps*_*lon 5 ruby io performance

简短版本: 如何在使用 Ruby 保持高性能的同时,逐个字符地从 STDIN(或文件)中读取?(尽管问题可能不是特定于 Ruby 的)

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

cat huge_text_file.txt | program.rb

input  > 123123sdas234sdsd5a ...
output > 123123, 234, 5, ...
Run Code Online (Sandbox Code Playgroud)

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

简单地逐个字符读取而不处理 900Kb 的输入文件大约需要 7 秒!

while c = STDIN.read(1)
end
Run Code Online (Sandbox Code Playgroud)

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

while s = STDIN.gets
end
Run Code Online (Sandbox Code Playgroud)

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

STDIN.gets被烧焦阅读焦炭内部,直到它遇到' \n“?

使用 C,我可能会分块读取数据,尽管我必须处理由缓冲区窗口拆分的数字,但这对于 Ruby 来说似乎不是一个优雅的解决方案。那么这样做的正确方法是什么?

PS 在 Python 中读取同一个文件的时间:

for line in f:
    line
f.close()
Run Code Online (Sandbox Code Playgroud)

运行时间为 0.01 秒。

c = f.read(1)
while c:
    c = f.read(1)
f.close()
Run Code Online (Sandbox Code Playgroud)

运行时间为 0.17 秒。

谢谢!

Eri*_*nil 4

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

同时记忆的单词不超过1000个。请注意,用作" "分隔符意味着“单词”可能包含换行符。

该脚本用于IO#each指定分隔符(在本例中为空格,以获取单词的枚举器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
Run Code Online (Sandbox Code Playgroud)

|您还可以直接读取文件,而不是使用 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
Run Code Online (Sandbox Code Playgroud)

使用这段代码,不会分割任何数字,不需要任何逻辑,它应该比逐个字符读取文件要快得多,并且比将整个文件读入字符串要使用更少的内存。