嵌入式 Linux 中的管道命令性能

嵌入式 Linux 中的管道命令性能

最近,我们购买了一个软件解决方案以集成到我们正在开发的设备中。我们需要修改和适应我们的需求,所以今天我浏览了部分代码,看看需要更改哪些内容,并开始思考一些问题。

在浏览一些剧本时,我看到了一些引起我注意的台词。例如,有一个是这样的:

cat file | grep ^field | head -n1 | sed 's/:/ /' | awk '{print $1}'

当您只需一次调用即可轻松完成所有操作时awk,这似乎有点愚蠢,也许类似于:

awk -F':' '/^field/ {print $1; exit}' file

后来我开始关注这个问题,发现很多类似的情况。有些我不关心,因为它们是在初始化期间运行的。另一方面,其他的则被频繁调用。

这意味着我可以在任何地方都有脚本生成进程来完成可以用更少的资源完成的任务。现在我实际上开始想知道......管道太多是否会在某个时候开始损害性能?特别是如果有较少的“管道式的“ 选择。

请记住,我在一个比实际 PC 资源少得多的平台上运行嵌入式 Linux。尽管出于问题的目的,也许这并不重要。

答案1

有时,分析事物更容易:

我创建了一个示例输入文件:

aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc
field:bbbbb:ccccc
aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc
aaaaa:bbbbb:ccccc

shell脚本'a.sh':

#!/bin/bash
for i in `seq 1 1000`; do
        cat test.dat | grep ^field | head -n1 | sed 's/:/ /' | awk '{print $1}' >/dev/null
done

shell脚本'b.sh':

#!/bin/bash
for i in `seq 1 1000`; do
        awk -F':' '/^field/ {print $1; exit}' test.dat >/dev/null
done

简介:

time ./a.sh
real    0m10.253s
user    0m5.526s
sys 0m8.668s

time ./b.sh
real    0m3.274s
user    0m1.288s
sys 0m1.783s

(这是在我心爱的 2008 MacBook、2.4 GHz Intel Core 2 Duo 上完成的)

很明显你的版本快了很多倍。然而,这些是 1000 次调用的时间。根据此 shell 代码的执行频率,您可能只会节省几毫秒。

答案2

性能很复杂。唯一确定的方法是在具有真实负载的真实系统上进行基准测试。

为多个公用设施输送管道肯定是有成本的。与字符串操作相比,这个成本是非常高的。然而,如果数据量足够大,管道解决方案可以更快,因为它可以允许专用工具更快地完成其工作,并且可以允许并行数据处理(如果机器是多核的并且有足够的有用处理可以并行完成以使其有价值)。但如果数据量很小,启动程序的成本就占主导地位。主导因素发生变化的点很大程度上取决于系统和使用场景。

一般来说,工具越通用,速度就越慢。因此,如果你有一项任务grep可以head完成,那么通常sed也可以完成,但速度不会那么快;并且awk会更慢。这只是一个经验法则;如果您去寻找,您将能够找到 awk 或 sed 击败其他工具的特定实现和特定工作负载。数据量必须足够高才能观察到差异。

对于小数据量,进程启动的数量是主要成本。一般来说,工具越通用,启动速度就越慢。启动多个工具本身就会产生性能成本,因为这意味着必须将更多代码加载到内存中。但是,如果您使用 BusyBox,其中所有工具都分组在一个可执行文件中,那么这一方面就会最小化。

从...开始

cat file | grep ^field | head -n1 | sed 's/:/ /' | awk '{print $1}'

的调用cat是没有用的,对性能没有帮助。如果数据量非常大,使用grep ^field | head -n1 | sed 's/:/ /' 可能会有一点优势,但在大多数情况下我希望

<file sed -n '/^field/ { s/:/ /p; q; }'

更快,因为它避免了等待多个进程。

至于 awk 调用,这里根本没有必要。如果没有前导冒号,则该命令相当于

<file sed -n '/^field/ { s/:.*//p; q; }'

或者,如果grep证明有优势,

<file grep '^field' | sed -e 's/:.*//' -e 'q'

如果有前导冒号,只需s/^::*//在命令开头添加即可sed

相关内容