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

时间:2012-07-25 01:03:11

标签: regex performance bash 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')

这里有效率吗?

5 个答案:

答案 0 :(得分:9)

简答

使用多个表达式比使用多个管道更快,因为在创建管道和分叉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

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

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

答案 1 :(得分:4)

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的单个实例,则会占用时间。

答案 2 :(得分:2)

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

答案 3 :(得分:0)

您可以测量效率来衡量不同的效果。也许使用time命令。根据经验,-e会更有效率。

答案 4 :(得分:0)

正如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文档中看到semicolon-as-sed-command-separator,并且不知道这是否在GNU sed以外的其他版本中可用。