linux - 如何在shell脚本的while循环中优化从大文件中读取
问题描述
我在网上浏览了一些关于如何优化文件输入到循环的随机文章,并尝试自己测试。他们声称,在大多数情况下,文件描述符操作比直接从文件读取到循环中更快、更有效。我试图测试它这样做:
首先从文件中直接读入循环:
time while read a ; do :;done < testfile
此命令运行所需的时间是:
real 0m8.782s
user 0m1.292s
sys 0m0.399s
现在我尝试做一些文件描述符操作,作为建议的文章之一:
- 我首先将文件描述符零重定向到文件描述符3,例如:
exec 3<&0
- 然后我将 testfile 重定向到文件描述符 0 :
exec 0 < testfile
在循环结束时,我正在读取数据,
0<&3
这意味着将文件描述符重定向3
到0
. 所以完整的行如下:exec 3<&0;exec 0<testfile; time for i in $(seq 1 20);do while read a; do :;done; done; exec 0<&3
这给了我一个时间:
real 0m8.792s
user 0m1.258s
sys 0m0.430s
但是我发现这两种情况下的时间几乎相同,实际上当我使用文件描述符时会慢一点。该文件testfile
的行数6MB
接近,400k
每行20-25
字符数最多。
事实上,对于更大的文件,从文件中直接读取实际上比文件描述符操作更快。
解决方案
使用 C。如果您真的关心速度,这是您可以获得的最快速度。
您可以从输入流编写自己的程序getline()
,然后system
在每一行上调用。由于fork()
andexec()
调用,这可能会更慢,但如果您可以将行操作放入 C 代码中,则可能会更快。
您可以编写自己的内置 shell。Shellsread
构建只是调用,在此处read()
浏览 bash 。您可以编写自己的内置shell,它通过输入循环命令比默认内置更快。喜欢。read
my_read_bultin 'file' -- 'command to run on each line'
为了让您发布可重现我创建了一个大文件:
$ for ((i=0;i<1200000;++i)); do echo ${RANDOM}; done >/tmp/1
$ du -hs /tmp/1
6.5M /tmp/1
然后运行:
$ time ( printf '#include<errno.h>\n#include<stdlib.h>\n#include<stdio.h>\nint main(){char*b=0;size_t n=0;ssize_t r;while((r=getline(&b,&n,stdin))>0);if(errno)abort();return 0;}\n' | gcc -Ofast -Wall -xc -o/tmp/a.out -; /tmp/a.out </tmp/1; )
real 0m0.095s
user 0m0.064s
sys 0m0.031s
$ time ( cat >/dev/null; ) </tmp/1
real 0m0.007s
user 0m0.001s
sys 0m0.006s
$ time ( while read l; do :; done </tmp/1; )
real 0m6.994s
user 0m5.222s
sys 0m1.731s
$ time ( exec 3</tmp/1; while read -u3 l; do :; done; )
real 0m7.953s
user 0m5.965s
sys 0m1.949s
$ time xargs -a /tmp/1 -n1 true
< very, very slow, got impatient and CTRL+C it >
推荐阅读
- javascript - 如何在漏斗高图中的顶部,左侧和底部添加文本
- r - 如何使用 kableExtra 增加下划线厚度?
- corda - Corda 验证如何用于多个州之间的交易?
- java - 从方法返回字符
- ios - 在嵌套闭包中声明弱
- vba - 如何删除后端数据库、VBA、Access中的SQL记录
- react-native - 消息仅显示在一侧(屏幕左侧) - react-native-gifted-chat
- unix - 基于第 1 列的第 2 列记录数不匹配
- javascript - JavaScript 内置 find() 函数不适用于 angularjs 1.7
- git - git中的樱桃采摘提交:大文件数据是否重复?