Вирішено в базі 5,0
Фон
Для передумови (і розуміння (і намагаючись уникнути низових подій це питання, схоже, приваблює)) я поясню шлях, який пройшов мене до цього питання (ну найкраще, що я можу згадати через два місяці).
Припустимо, ви робите кілька тестів на оболонку для списку символів Unicode:
printf "$(printf '\\U%x ' {33..200})"
і там є більше ніж 1 мільйон символів Unicode, тестування 20 000 з них, здається, не так вже й багато.
Також припустимо, що ви задаєте символи як позиційні аргументи:
set -- $(printf "$(printf '\\U%x ' {33..20000})")
з наміром передати персонажів до кожної функції, щоб обробити їх різними способами. Отже функції повинні мати форму test1 "$@"
або подібну. Тепер я розумію, наскільки це погана ідея в баші.
Тепер, припустимо, що для вирішення питання, який є кращим, потрібно вчасно (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, не багато.
Щоб спробувати себе, видаліть усі "$@"
функції ввімкнення main1
(або зробіть копію) і повторіть тест (або обидва, main1
і копію main2
(з main2 "$@"
)) для порівняння. Це основна структура нижче в початковому дописі (ОП).
Але я задумався: чому оболонці потрібно так довго, щоб "нічого не робити" ?. Так, лише «пару секунд», але все ж, чому ?.
Це змусило мене випробувати в інших снарядах, щоб виявити, що тільки баш мав цю проблему.
Спробуйте ksh ./script
(той самий сценарій, що і вище).
Це призводить до цього опису: виклик функції ( test#
) без жодних аргументів затримується аргументами в батьківському ( main#
). Це опис, який випливає, і був початковим повідомленням (OP) нижче.
Оригінальна публікація.
Виклик функції (в Bash 4.4.12 (1) -випуск) робити нічого 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
або обробляючий аргумент список не є джерелом затримки.
1 Цевідомощо передача результатів аргументів збільшує час виконання. Дякуємо@slm