在awk中使用pipe|时的性能注意事项


awk -F'/' '{ print $1 |" sort " }' infile > outfile

awk -F'/' '{ print $1 }' infile | sort > outfile

如果我在awk中使用管道(或重定向),这些MVCE是否完全等效,或者是否存在我不知道的可移植性/性能问题。

两个命令都会产生正确的输出。

更新:我自己做了一些研究——见下面的答案。

tl;dr在awk中使用管道的速度可能是的两倍

我去看了一下gawk源中的io.c

只要不使用协同过程,使用awk的管道就是POSIX。即|&

如果你的操作系统不支持管道(评论中提到了这一点),gawk会像你预期的那样通过写入文件来模拟管道。这需要一段时间,但至少你有管道,而你没有。

如果你有一个真正的操作系统,它会派生子系统并在那里编写输出,所以你不会期望在awk中使用管道会带来巨大的性能下降。

有趣的是,gawk对等简单情况进行了一些优化

awk '{print $1}'

所以我运行了一个测试用例。

for i in $(seq 1 10000000); do echo $(( 10000000-$i )) " " $i;done > infile

1000万条记录似乎足以消除系统中其他工作的差异。

然后

time awk '{ print $1 }' infile | sort -n > /dev/null
real    0m10.350s
user    0m7.770s
sys     0m3.000s

或平均约为。

time awk '{ print $1 | " sort -n " }' infile > /dev/null
real    0m25.870s
user    0m13.880s
sys     0m13.030s

正如你所看到的,这是一个巨大的差异。

因此得出结论:
尽管它可能会慢得多,但在许多用例中,收益远远超过额外的性能打击。实际上,只有在像MVCE这样的简单情况下,你才应该把管道放在外面。

这里讨论了重定向到awk与使用文件名调用awk之间的区别。虽然没有直接的关系,但如果你读到这里,可能会感兴趣。

如果在awk中使用|,打印语句的输出将累积为一个字符串,然后使用该字符串执行"xxx"中的shell命令。

考虑:

$ echo 1 4 2 3 | awk '{for (i=1; i<=NF; i++) print $i}'
1
4
2
3

现在试试:

$ echo 1 4 2 3 | awk '{for (i=1; i<=NF; i++) print $i | "sort" }' 
1
2
3
4

1n4n2n3的单个字符串在内部构造,然后通过awk传递给sort。这可以组合成更复杂的调用,例如:

awk '{ print $1 > "names.unsorted"
command = "sort -r > names.sorted"
print $1 | command }' names

更多关于重定向的GNU awk手册。

最新更新