流水线sed调用和多个sed表达式之间的效率是否存在差异



我有一个关于bash中sed效率的问题。我有一系列流水线式的sed语句,例如:

var1="Some string of text"
var2=$(echo "$var1" | sed 's/pattern1/replacement1/g' | sed 's/pattern2/replacement2/g' | sed 's/pattern3/replacement3/g' | sed 's/pattern4/replacement4' | sed 's/pattern5/replacement5/g')

假设没有任何输入依赖于早期sed管道的编辑输出,那么我是否更适合用表达式语句编写上面的脚本?例如:

var2=$(echo "$var1" | sed -e's/pattern1/replacement1/g' -e's/pattern2/replacement2/g' -e's/pattern3/replacement3/g' -e's/pattern4/replacement4/g' -e's/pattern5/replacement5/g')

这里有效率吗?

简短回答

使用多个表达式将比使用多个管道更快,因为在创建管道和分叉sed进程时会有额外的开销。然而,这在实践中很少有足够的区别。

基准

使用多个表达式比使用多个管道更快,但对于一般用例来说可能还不够重要。使用您的示例,执行速度的平均差异只有千分之二秒,这还不足以让人兴奋。

# Average run with multiple pipelines.
$ time {
    echo "$var1" | 
    sed 's/pattern1/replacement1/g' |
    sed 's/pattern2/replacement2/g' |
    sed 's/pattern3/replacement3/g' |
    sed 's/pattern4/replacement4/g' |
    sed 's/pattern5/replacement5/g'
}
Some string of text
real        0m0.007s
user        0m0.000s
sys         0m0.004s

# Average run with multiple expressions.
$ time {
    echo "$var1" | sed 
    -e 's/pattern1/replacement1/g' 
    -e 's/pattern2/replacement2/g' 
    -e 's/pattern3/replacement3/g' 
    -e 's/pattern4/replacement4/g' 
    -e 's/pattern5/replacement5/g'
}
Some string of text
real        0m0.005s
user        0m0.000s
sys         0m0.000s

当然,这不是针对大型输入文件、数千个输入文件进行测试,也不是在具有数万次迭代的循环中运行。尽管如此,可以肯定地说,这种差异很小,与大多数常见情况无关。

不常见的情况则不同。在这种情况下,基准测试将帮助您确定用内联表达式替换管道是否是该用例的有价值的优化

sed中的大部分开销倾向于处理正则表达式,但在每个示例中处理的正则表达式数量相同。

考虑操作系统需要为管道的每个元素构造std和stdout。Sed还占用系统中的内存,操作系统必须为Sed的每个实例分配内存——无论是一个实例还是四个实例。

以下是我的评估:

$ jot -r 1000000 1 10000 | time sed 's/1/_/g' | time sed 's/2/_/g' | time sed 's/3/_/g' | time sed 's/4/_/g' >/dev/null 
        2.38 real         0.84 user         0.01 sys
        2.38 real         0.84 user         0.01 sys
        2.39 real         0.85 user         0.01 sys
        2.39 real         0.85 user         0.01 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.57 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.56 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.57 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.74 real         2.57 user         0.02 sys
$ dc
.84 2* .85 2* + p
3.38
$ 

由于3.38>2.57,如果使用sed的单个实例,则会占用les时间。

是。你将避免每次重新开始sed的开销。

您可能可以测量效率来衡量不同的效率。可能使用time命令。从经验上讲,-e会更有效。

正如ghoti的回答中所指出的,在任何一种情况下,您的示例都有相同数量的正则表达式(分别调用sed和一系列-e表达式),但操作系统开销包括管道和进程设置以及sed的每个实例的内存分配。对于少数调用,操作系统开销不值得担心,但如果数量是数千或更多,则可能是。

无论如何,撇开计算机效率不谈,程序员的效率往往是一个更重要的问题。到目前为止,这两种方式都很笨拙,进入速度也很慢。使用分号分隔的sed命令列表而不是许多单独的-e字符串更容易(至少对于GNU sed)。下面是一个例子。

$ var1="Some p1 string p2 of p3 text p4 etc"
$ var2=$(echo "$var1" | sed 's/p1/a1/g; s/p2/b2/g; s/p3/c3/g; s/p4/d4/; s/p5/e5/g')
$ echo $var2
Some a1 string b2 of c3 text d4 etc

不幸的是,我没有在sed文档中看到分号作为sed命令分隔符,也不知道这在GNU sed之外的其他版本中是否可用。

最新更新