Antecedentes
Para o fundo (e compreensão (e tentar evitar as opiniões negativas que esta pergunta parece atrair)) Vou explicar o caminho que me levou a esta questão (bem, o melhor que me lembro dois meses depois).
Suponha que você esteja fazendo alguns testes de shell para uma lista de caracteres Unicode:
printf "$(printf '\U%x ' {33..200})"
e existindo mais de 1 milhão de caracteres Unicode, testar 20.000 deles não parece ser muito.
Suponha também que você defina os caracteres como argumentos posicionais:
set -- $(printf "$(printf '\U%x ' {33..20000})")
com a intenção de passar os caracteres para cada função para processá-los de maneiras diferentes. Portanto, as funções devem ter o formato test1 "$@"
ou similar. Agora eu percebo como essa idéia é ruim no bash.
Agora, suponha que existe a necessidade de tempo (n = 1000) em cada solução para descobrir qual é o melhor, sob tais condições você terminará com uma estrutura similar a:
#!/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 "$@"
As funções test#
são muito simples para serem apresentadas aqui.
Os originais foram progressivamente reduzidos para descobrir onde estava o enorme atraso.
O script acima funciona, você pode executá-lo e perder alguns segundos fazendo muito pouco.
No processo de simplificar para encontrar exatamente onde o atraso foi (e reduzir cada função de teste a quase nada é o extremo depois de muitas tentativas), decidi remover a passagem de argumentos para cada função de teste para descobrir quanto tempo melhorado, apenas um fator de 6, não muito.
Para experimentar, remova todo o "$@"
na função main1
(ou faça uma cópia) e teste novamente (ou ambos main1
e a cópia main2
(com main2 "$@"
)) para comparar. Esta é a estrutura básica abaixo no post original (OP).
Mas eu me perguntava: por que a casca demorou tanto para "não fazer nada" ?.
Sim, apenas "alguns segundos", mas ainda assim, por que?.
Isso me fez testar em outros shells para descobrir que apenas o bash tinha esse problema.
Tente ksh ./script
(o mesmo script acima).
Isso leva a esta descrição: chamar uma função ( test#
) sem que nenhum argumento seja atrasado pelos argumentos no pai ( main#
). Esta é a descrição que segue e foi o post original (OP) abaixo.
postagem original.
Chamar uma função (no Bash 4.4.12 (1) -release) para não fazer nada f1(){ :; }
é mil vezes mais lenta que :
mas somente se houver argumentos definidos no < função de chamada strong> parent , por quê?
#!/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
Resultados de 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
Não há argumentos nem entrada nem saída usados na função f1
, o atraso de um fator de mil (1000) é inesperado. 1
Estendendo os testes para vários shells, os resultados são consistentes, a maioria dos shells não tem problemas nem sofrem atrasos (os mesmos n e m são usados):
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
Resultados:
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 ......
Descomente os outros dois testes para confirmar que nem seq
ou o processamento da lista de argumentos é a origem do atraso.
1 sabe-se que passar resultados por argumentos aumentará o tempo de execução . Obrigado @slm