Bash 5.0'da çözüldü
Arka fon
Arka plan (ve anlayış için (ve bu soruyu aşağı çekmekten kaçınmaya çalışıyorum) beni bu soruna götüren yolu açıklayacağım (iyi, iki ay sonra hatırlayabileceğim en iyisi).
Unicode karakterlerin listesi için bazı kabuk testleri yaptığınızı varsayın:
printf "$(printf '\\U%x ' {33..200})"
ve 1 milyondan fazla Unicode karakter olduğu için 20.000'i test etmek o kadar da görünmüyor.
Ayrıca karakterleri konum bağımsız değişkenleri olarak ayarladığınızı varsayın:
set -- $(printf "$(printf '\\U%x ' {33..20000})")
karakterleri farklı şekillerde işlemek için her bir işleve aktarmak amacıyla. Bu nedenle işlevler biçime test1 "$@"
veya benzerine sahip olmalıdır . Şimdi bunun bash'ta ne kadar kötü bir fikir olduğunu anlıyorum.
Şimdi, hangisinin daha iyi olduğunu bulmak için her bir çözeltinin zamana (n = 1000) ihtiyaç olduğunu varsayın, bu koşullar altında aşağıdakine benzer bir yapı ile bitireceksiniz:
#!/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 "$@"
Fonksiyonlar test#
burada sunulmak için çok basit hale getirilmiştir.
Orijinaller, büyük gecikmenin nerede olduğunu bulmak için aşamalı olarak kesildi.
Yukarıdaki komut dosyası çalışır, çalıştırabilir ve çok az şey yaparak birkaç saniye harcayabilirsiniz.
Gecikmenin tam olarak nerede olduğunu bulmak için basitleştirme sürecinde (ve birçok testten sonra her test fonksiyonunu neredeyse hiçbir şeye indirgemek aşırıdır), zamanın ne kadar iyileştiğini öğrenmek için argümanların her test fonksiyonuna geçişini kaldırmaya karar verdim, sadece 6 faktörü, fazla değil.
Kendinizi denemek için, tüm "$@"
giriş işlevini kaldırın main1
(veya bir kopyasını oluşturun) ve karşılaştırmak için tekrar test edin (veya her ikisini main1
ve kopyasını main2
(ile main2 "$@"
)). Bu, orijinal yazıda (OP) aşağıdaki temel yapıdır.
Ama merak ettim: kabuk neden "hiçbir şey yapmamak" için bu kadar uzun sürüyor? Evet, sadece "birkaç saniye", ama yine de neden?
Bu, beni sadece bash'ın bu sorunu yaşadığını keşfetmek için diğer kabuklarda test etmemi sağladı.
Deneyin ksh ./script
(yukarıdakiyle aynı komut dosyasını).
Bu şu açıklamaya yol açar: test#
bağımsız değişken olmadan bir function ( ) çağrılması parent ( main#
) içindeki bağımsız değişkenler tarafından ertelenir . Bu, aşağıdaki açıklamadır ve aşağıdaki orijinal gönderidir (OP).
Orijinal gönderi.
Bir işlevi yapmak için bir işlevi çağırmak (Bash 4.4.12 (1) -çalışma), f1(){ :; }
bin kat daha yavaştır, :
ancak yalnızca üst arama işlevinde tanımlanmış bağımsız değişkenler varsa , Neden?
#!/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
Sonuçları 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
İşlevde kullanılan herhangi bir argüman veya girdi veya çıktı yoktur f1
, bin faktörün gecikmesi (1000) beklenmediktir. 1
Testleri birkaç mermiye genişletmek, sonuçlar tutarlıdır, çoğu mermide sorun yoktur veya gecikme yaşanmaz (aynı n ve m kullanılır):
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
Sonuçlar:
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
Bağımsız değişken listesinin ya da işlenmesinin gecikmenin kaynağı olmadığını doğrulamak için diğer iki testi de kaldırın.
1. argümanlar sonuçları geçen uygulama süresini artacağı bilinmektedir. Teşekkürler@slm