首页 > 解决方案 > 如何在shell脚本的while循环中优化从大文件中读取

问题描述

我在网上浏览了一些关于如何优化文件输入到循环的随机文章,并尝试自己测试。他们声称,在大多数情况下,文件描述符操作比直接从文件读取到循环中更快、更有效。我试图测试它这样做:

首先从文件中直接读入循环:

time while read a ; do :;done < testfile

此命令运行所需的时间是:

real 0m8.782s
user 0m1.292s
sys  0m0.399s

现在我尝试做一些文件描述符操作,作为建议的文章之一:

  1. 我首先将文件描述符零重定向到文件描述符3,例如:exec 3<&0
  2. 然后我将 testfile 重定向到文件描述符 0 :exec 0 < testfile
  3. 在循环结束时,我正在读取数据,0<&3这意味着将文件描述符重定向30. 所以完整的行如下:

    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字符数最多。

事实上,对于更大的文件,从文件中直接读取实际上比文件描述符操作更快。

标签: linuxshellscripting

解决方案


使用 C。如果您真的关心速度,这是您可以获得的最快速度。

您可以从输入流编写自己的程序getline(),然后system在每一行上调用。由于fork()andexec()调用,这可能会更慢,但如果您可以将行操作放入 C 代码中,则可能会更快。
您可以编写自己的内置 shell。Shellsread构建只是调用,在此处read()浏览 bash 。您可以编写自己的内置shell,它通过输入循环命令比默认内置更快。喜欢。readmy_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 >

推荐阅读