有时例子是有帮助的。
f='fred';y=0;time for ((i=0;i<1000;i++));do if [[ -n "$( grep 're' <<< $f )" ]];then ((y++));fi;done;echo $y
real 0m3.878s
user 0m0.794s
sys 0m2.346s
1000
f='fred';y=0;time for ((i=0;i<1000;i++));do if [[ -z "${f/*re*/}" ]];then ((y++));fi;done;echo $y
real 0m0.041s
user 0m0.027s
sys 0m0.001s
1000
f='fred';y=0;time for ((i=0;i<1000;i++));do if grep -q 're' <<< $f ;then ((y++));fi;done >/dev/null;echo $y
real 0m2.709s
user 0m0.661s
sys 0m1.731s
1000
如您所见,在这种情况下,在子 shell 中使用 grep 和使用参数扩展来执行相同的基本测试之间的差异在总时间上接近 100 倍。
进一步追问问题,并考虑到下面的评论,这些评论显然未能表明他们试图表明什么,我检查了以下代码:
https ://unix.stackexchange.com/questions/284268/what-is-the -使用子shell的开销
time for((i=0;i<10000;i++)); do echo "$(echo hello)"; done >/dev/null
real 0m12.375s
user 0m1.048s
sys 0m2.822s
time for((i=0;i<10000;i++)); do echo hello; done >/dev/null
real 0m0.174s
user 0m0.165s
sys 0m0.004s
这实际上比我预期的要糟糕得多。事实上,总时间慢了近两个数量级,系统调用时间慢了近三个数量级,这绝对令人难以置信。
https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html
请注意,演示这一点的目的是表明,如果您使用的测试方法很容易养成使用习惯,则可以使用 subshell grep 或 sed 或 gawk(或内置 bash,如 echo),它适用于我是一个坏习惯,我在快速破解时往往会养成,值得意识到这将对性能产生重大影响,如果 bash 内置程序可以本地处理这项工作,那么可能值得花时间避免这些。
通过仔细检查使用子shell 的大型程序,并在可能的情况下用其他方法替换它们,我能够在刚刚完成的一组优化中减少大约 10% 的总执行时间(不是第一个,也不是最后一个,我做这个的时候,已经优化了好几次了,所以再增加10%其实很重要)
所以值得警惕。
因为我很好奇,所以我想在这里确认“时间”告诉我们什么:
https://en.wikipedia.org/wiki/Time_(Unix)
总 CPU 时间是 CPU 或 CPU 为程序执行某些操作所花费的时间与代表程序为内核执行系统调用所花费的时间的组合。当程序循环遍历数组时,它正在累积用户 CPU 时间。相反,当程序执行诸如 exec 或 fork 之类的系统调用时,它正在累积系统 CPU 时间。
正如您在回声循环测试中所看到的,分叉的成本在对内核的系统调用方面非常高,这些分叉确实加起来(700 倍!!!更多的时间花在系统调用上)。
我正在解决其中一些问题,所以这些问题实际上与我以及喜欢该程序的全球用户社区非常相关,也就是说,这对我来说不是一个神秘的学术点,这是真实的世界,具有真实的影响。