5

我对 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')

这里有什么效率可以提高吗?

4

5 回答 5

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

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

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

于 2012-07-25T01:30:54.593 回答
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 实例,则占用的时间更少。

于 2012-07-25T01:12:17.750 回答
2

是的。您将避免每次重新启动 sed 的开销。

于 2012-07-25T01:04:42.620 回答
0

您可能可以衡量效率以衡量不同之处。也许使用time命令。根据经验, -e 会更有效。

于 2012-07-25T01:14:05.970 回答
0

正如 ghoti 的回答中所述,您的示例在任何一种情况下都具有相同数量的正则表达式(单独调用sedvs 一系列-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-command-separator sed,并且不知道这在 GNU sed 之外的其他版本中是否可用。

于 2012-09-16T15:27:54.937 回答