在bash 5.0中解决
背景
对于背景(和理解(并试图避免这个问题似乎吸引人们的投票)),我将解释导致该问题的途径(嗯,两个月后我能记得的最好的)。
假设您正在对一些Unicode字符进行shell测试:
printf "$(printf '\\U%x ' {33..200})"
并且有超过一百万个Unicode字符,测试其中的20.000个字符似乎并不多。
还假设您将字符设置为位置参数:
set -- $(printf "$(printf '\\U%x ' {33..20000})")
目的是将字符传递给每个函数以不同的方式处理它们。因此,函数应具有形式test1 "$@"
或类似形式。现在,我意识到这在bash中是多么糟糕的主意。
现在,假设需要时间(n = 1000)每个解决方案以找出哪个更好,在这种情况下,您将得到一个类似于以下的结构:
#!/bin/bash --
TIMEFORMAT='real: %R' # '%R %U %S'
set -- $(printf "$(printf '\\U%x ' {33..20000})")
n=1000
test1(){ echo "$1"; } >/dev/null
test2(){ echo "$#"; } >/dev/null
test3(){ :; }
main1(){ time for i in $(seq $n); do test1 "$@"; done
time for i in $(seq $n); do test2 "$@"; done
time for i in $(seq $n); do test3 "$@"; done
}
main1 "$@"
这些功能test#
非常简单,只在此处介绍。
原稿被逐步修整,以找出巨大的延迟在哪里。
上面的脚本起作用了,您可以运行它,花点时间做很少的事情。
在简化以精确找到延迟的位置的过程中(在多次试验后将每个测试函数减少到几乎没有是极端的),我决定删除传递给每个测试函数的参数以找出改善的时间,只是6倍,不多。
要尝试一下,请删除所有"$@"
in函数main1
(或进行复制),然后再次进行测试(或同时进行测试main1
和复制main2
(使用main2 "$@"
))进行比较。这是原始帖子(OP)下面的基本结构。
但是我想知道:为什么壳要花这么长时间才能“什么也不做”?是的,只有“几秒钟”,但是为什么呢?
这使我在其他shell中进行测试,以发现只有bash出现了此问题。
尝试ksh ./script
(与上面相同的脚本)。
这导致了这样的描述:test#
不带任何参数的函数()会被父(main#
)中的参数延迟。这是下面的描述,并且是下面的原始文章(OP)。
原始帖子。
调用一个函数(Bash中4.4.12(1)-release)什么也不做,f1(){ :; }
是一千倍速度低于:
但仅如果在规定的参数父调用函数,为什么呢?
#!/bin/bash
TIMEFORMAT='real: %R'
f1 () { :; }
f2 () {
echo " args = $#";
printf '1 function no args yes '; time for ((i=1;i<$n;i++)); do : ; done
printf '2 function yes args yes '; time for ((i=1;i<$n;i++)); do f1 ; done
set --
printf '3 function yes args no '; time for ((i=1;i<$n;i++)); do f1 ; done
echo
}
main1() { set -- $(seq $m)
f2 ""
f2 "$@"
}
n=1000; m=20000; main1
结果test1
:
args = 1
1 function no args yes real: 0.013
2 function yes args yes real: 0.024
3 function yes args no real: 0.020
args = 20000
1 function no args yes real: 0.010
2 function yes args yes real: 20.326
3 function yes args no real: 0.019
函数中没有自变量,也没有输入或输出f1
,千倍(1000)的延迟是意外的。1个
将测试扩展到几个外壳程序,结果是一致的,大多数外壳程序都没有问题也没有延迟(使用相同的n和m):
test2(){
for sh in dash mksh ksh zsh bash b50sh
do
echo "$sh" >&2
# \time -f '\t%E' seq "$m" >/dev/null
# \time -f '\t%E' "$sh" -c 'set -- $(seq '"$m"'); for i do :; done'
\time -f '\t%E' "$sh" -c 'f(){ :;}; while [ "$((i+=1))" -lt '"$n"' ]; do : ; done;' $(seq $m)
\time -f '\t%E' "$sh" -c 'f(){ :;}; while [ "$((i+=1))" -lt '"$n"' ]; do f ; done;' $(seq $m)
done
}
test2
结果:
dash
0:00.01
0:00.01
mksh
0:00.01
0:00.02
ksh
0:00.01
0:00.02
zsh
0:00.02
0:00.04
bash
0:10.71
0:30.03
b55sh # --without-bash-malloc
0:00.04
0:17.11
b56sh # RELSTATUS=release
0:00.03
0:15.47
b50sh # Debug enabled (RELSTATUS=alpha)
0:04.62
xxxxxxx More than a day ......
取消对其他两个测试的注释,以确认两者都不是seq
或处理参数列表是延迟的源。