删除文件第一行的sedvs性能tail

删除文件第一行的sedvs性能tail

在这个答案中(如何使用 sed 删除文件的第一行?)有两种方法可以删除文件中的第一条记录:

sed '1d' $file >> headerless.txt

** - - - - - - - - 或者 - - - - - - - - **

tail -n +2 $file >> headerless.txt

就我个人而言,我认为这个tail选项在外观上更令人愉悦且更具可读性,但可能是因为我对 sed 不太熟悉。

哪种方法最快?

答案1

删除文件第一行的sedvs性能tail

总结

  • sed功能强大,用途广泛,但这也导致它运行缓慢,特别是对于包含多行数据的大文件。

  • tail只做一件简单的事情,但是它做得很好,速度很快,即使对于有很多行的大文件也是如此。

对于中小型文件,sedtail的性能同样快(或慢,取决于您的期望)。但是,对于较大的输入文件(多个 MB),性能差异会显著增加(对于数百 MB 范围内的文件,性能差异会显著增加), 的tail性能明显优于sed

实验

一般准备:

我们要分析的命令是:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

请注意,我每次都将输出传输到管道,/dev/null以消除终端输出或文件写入作为性能瓶颈。

让我们设置一个 RAM 磁盘来消除磁盘 I/O 的潜在瓶颈。我个人有一个tmpfs安装在/tmp,所以我只是把它放在testfile那里进行这个实验。

然后我曾经$numoflines使用此命令创建一个随机测试文件,其中包含指定数量的行,具有随机行长度和随机数据(请注意,这肯定不是最佳的,当大约> 2M行时它会变得非常慢,但谁在乎,这不是我们正在分析的东西):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

哦,顺便说一句。我的测试笔记本电脑在 Intel i5-6200U CPU 上运行 Ubuntu 16.04,64 位。仅用于比较。

对大文件进行计时:

设置一个巨大的testfile

运行上述命令会numoflines=10000000产生一个包含 10M 行的随机文件,占用超过 600 MB 的空间 - 它相当大,但让我们从它开始,因为我们可以:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

使用我们巨大的执行计时运行testfile

现在让我们首先使用这两个命令进行一次定时运行,以估计我们正在工作的幅度。

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

对于大文件,我们已经看到了非常明显的效果,tail比 快一个数量级sed。但只是为了好玩,并确保没有随机的副作用造成很大的差异,让我们重复 100 次:

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

结论是一样的,sed删除大文件的第一行效率很低,tail应该在那里使用。

sed是的,我知道 Bash 的循环结构很慢,但是我们在这里只进行相对较少的迭代,而且与/运行时间相比,普通循环所花费的时间并不重要tail

对小文件进行计时:

设置一个小的testfile

现在为了完整性,让我们看一下更常见的情况,即您有一个在 kB 范围内的小输入文件。让我们创建一个随机输入文件numoflines=100,如下所示:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

使用我们的小程序进行计时运行testfile

根据经验,我们可以预计这种小文件的处理时间在几毫秒的范围内,所以我们立即进行 1000 次迭代:

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

如您所见,时间非常相似,没有太多需要解释或疑惑的地方。对于小文件,这两种工具同样适用。

答案2

这是另一种选择,仅使用 bash 内置命令和cat

{ read ; cat > headerless.txt; } < $file

$file重定向到{ }命令分组。它read只是读取并丢弃第一行。然后,将流的其余部分通过管道传输到cat目标文件,并将其写入目标文件。

在我的 Ubuntu 16.04 上,这个和解决方案的性能tail非常相似。我创建了一个较大的测试文件,其中包含以下内容seq

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail解决方案:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/支撑解决方案:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

虽然我现在手边只有一台 Ubuntu VM,并且发现两者的时间存在很大差异,尽管它们都在同一个范围内。

答案3

在我的系统上尝试,并在每个命令前面加上前缀,time我得到了以下结果:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

和尾巴:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

这表明,在我的系统(至少是运行 Ubuntu 16.04 的 AMD FX 8250)上,tail 的速度明显更快。测试文件有 10,000 行,大小为 540k。该文件是从 HDD 读取的。

答案4

最佳答案没有考虑磁盘> /dev/null

如果你有一个大文件,并且不想在磁盘上创建临时副本,请尝试vim -c

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

编辑:如果文件大于可用内存则vim -c不起作用,看起来它不够智能,无法对文件进行增量加载

相关内容