gpt4 book ai didi

ruby - 为什么 "slurping"文件不是一个好习惯?

转载 作者:数据小太阳 更新时间:2023-10-29 06:24:46 28 4
gpt4 key购买 nike

为什么“slurping”文件不是普通文本文件 I/O 的好做法,什么时候有用?

例如,为什么我不应该使用这些?

File.read('/path/to/text.txt').lines.each do |line|
# do something with a line
end

File.readlines('/path/to/text.txt').each do |line|
# do something with a line
end

最佳答案

我们一次又一次地看到询问有关读取文本文件以逐行处理它的问题,这些问题使用 readreadlines 的变体,它们将一次将整个文件存入内存。

The documentation for read 说:

Opens the file, optionally seeks to the given offset, then returns length bytes (defaulting to the rest of the file). [...]

The documentation for readlines 说:

Reads the entire file specified by name as individual lines, and returns those lines in an array. [...]

拉入一个小文件没什么大不了的,但是随着传入数据缓冲区的增长,内存必须重新排列,这会占用 CPU 时间。此外,如果数据占用太多空间,操作系统必须介入以保持脚本运行并开始假脱机到磁盘,这将使程序瘫痪。在 HTTPd(网络主机)或需要快速响应的东西上,它会削弱整个应用程序。

Slurping 通常是基于对文件 I/O 速度的误解,或者认为读取然后拆分缓冲区比一次读取一行更好。

下面是一些测试代码来演示由“slurping”引起的问题。

将其保存为“test.sh”:

echo Building test files...

yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000 > kb.txt
yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000000 > mb.txt
yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000000000 > gb1.txt
cat gb1.txt gb1.txt > gb2.txt
cat gb1.txt gb2.txt > gb3.txt

echo Testing...

ruby -v

echo
for i in kb.txt mb.txt gb1.txt gb2.txt gb3.txt
do
echo
echo "Running: time ruby readlines.rb $i"
time ruby readlines.rb $i
echo '---------------------------------------'
echo "Running: time ruby foreach.rb $i"
time ruby foreach.rb $i
echo
done

rm [km]b.txt gb[123].txt

它创建了五个大小递增的文件。 1K 的文件很容易处理,而且很常见。以前1MB的文件都算大,现在很普遍了。 1GB 在我的环境中很常见,并且会定期遇到超过 10GB 的文件,因此了解 1GB 及以上发生的情况非常重要。

将其保存为“readlines.rb”。除了在内部逐行读取整个文件,并将其附加到一个数组然后返回,它什么都不做,而且看起来很快,因为它都是用 C 编写的:

lines = File.readlines(ARGV.shift).size
puts "#{ lines } lines read"

将其保存为“foreach.rb”:

lines = 0
File.foreach(ARGV.shift) { |l| lines += 1 }
puts "#{ lines } lines read"

在我的笔记本电脑上运行 sh ./test.sh 我得到:

Building test files...
Testing...
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

读取1K文件:

Running: time ruby readlines.rb kb.txt
28 lines read

real 0m0.998s
user 0m0.386s
sys 0m0.594s
---------------------------------------
Running: time ruby foreach.rb kb.txt
28 lines read

real 0m1.019s
user 0m0.395s
sys 0m0.616s

读取 1MB 文件:

Running: time ruby readlines.rb mb.txt
27028 lines read

real 0m1.021s
user 0m0.398s
sys 0m0.611s
---------------------------------------
Running: time ruby foreach.rb mb.txt
27028 lines read

real 0m0.990s
user 0m0.391s
sys 0m0.591s

读取1GB文件:

Running: time ruby readlines.rb gb1.txt
27027028 lines read

real 0m19.407s
user 0m17.134s
sys 0m2.262s
---------------------------------------
Running: time ruby foreach.rb gb1.txt
27027028 lines read

real 0m10.378s
user 0m9.472s
sys 0m0.898s

读取2GB文件:

Running: time ruby readlines.rb gb2.txt
54054055 lines read

real 0m58.904s
user 0m54.718s
sys 0m4.029s
---------------------------------------
Running: time ruby foreach.rb gb2.txt
54054055 lines read

real 0m19.992s
user 0m18.765s
sys 0m1.194s

读取3GB文件:

Running: time ruby readlines.rb gb3.txt
81081082 lines read

real 2m7.260s
user 1m57.410s
sys 0m7.007s
---------------------------------------
Running: time ruby foreach.rb gb3.txt
81081082 lines read

real 0m33.116s
user 0m30.790s
sys 0m2.134s

请注意每次文件大小增加时 readlines 的运行速度都会减慢两倍,而使用 foreach 会线性减慢。在 1MB 处,我们可以看到有一些东西影响了“吞吐”的 I/O,但不影响逐行读取。而且,由于现在 1MB 的文件非常普遍,如果我们不提前考虑,很容易看出它们会在程序的整个生命周期中减慢文件的处理速度。这里几秒钟,或者当它们发生一次时不会太多,但如果它们在一分钟内发生多次,到一年年底就会对性能产生严重影响。

我几年前在处理大型数据文件时遇到过这个问题。我使用的 Perl 代码会定期停止,因为它会在加载文件时重新分配内存。重写代码而不是吞噬数据文件,而是逐行读取和处理它,这极大地提高了运行速度,从五分钟多到不到一分钟,给我上了很大的一课。

“读取”文件有时很有用,尤其是当您必须跨行执行某些操作时,但是,如果必须这样做,花一些时间考虑读取文件的替代方法是值得的。例如,考虑维护一个从最后“n”行构建的小缓冲区并扫描它。这将避免因试图读取和保存整个文件而导致的内存管理问题。这在与 Perl 相关的博客“Perl Slurp-Eaze”中进行了讨论,其中涵盖了“何时”和“为什么”以证明使用完整文件读取是合理的,并且适用于 Ruby。

出于其他不“吞噬”文件的充分理由,请阅读“How to search file text for a pattern and replace it with a given value”。

关于ruby - 为什么 "slurping"文件不是一个好习惯?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25189262/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com