Đã giải quyết trong bash 5.0
Lý lịch
Đối với nền tảng (và sự hiểu biết (và cố gắng tránh các câu hỏi hạ thấp câu hỏi này dường như thu hút)) tôi sẽ giải thích con đường đưa tôi đến vấn đề này (tốt nhất, tôi có thể nhớ lại hai tháng sau).
Giả sử bạn đang thực hiện một số kiểm tra trình bao cho danh sách các ký tự Unicode:
printf "$(printf '\\U%x ' {33..200})"
và có hơn 1 triệu ký tự Unicode, thử nghiệm 20.000 trong số chúng dường như không nhiều.
Cũng giả sử rằng bạn đặt các ký tự làm đối số vị trí:
set -- $(printf "$(printf '\\U%x ' {33..20000})")
với ý định chuyển các ký tự cho từng hàm để xử lý chúng theo các cách khác nhau. Vì vậy, các chức năng nên có hình thức test1 "$@"
hoặc tương tự. Bây giờ tôi nhận ra ý tưởng tồi tệ như thế nào trong bash.
Bây giờ, giả sử rằng cần có thời gian (một n = 1000) mỗi giải pháp để tìm ra giải pháp nào tốt hơn, trong những điều kiện như vậy, bạn sẽ kết thúc với một cấu trúc tương tự như:
#!/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 "$@"
Các chức năng test#
được thực hiện rất rất đơn giản chỉ để được trình bày ở đây.
Các bản gốc đã dần dần được cắt giảm để tìm nơi chậm trễ lớn.
Kịch bản trên hoạt động, bạn có thể chạy nó và lãng phí vài giây làm rất ít.
Trong quá trình đơn giản hóa để tìm chính xác độ trễ ở đâu (và giảm từng hàm kiểm tra xuống gần như không có gì là cực đoan sau nhiều thử nghiệm), tôi quyết định loại bỏ việc truyền các đối số cho từng hàm kiểm tra để tìm hiểu xem thời gian được cải thiện bao nhiêu hệ số 6, không nhiều.
Để tự mình thử, hãy xóa tất cả "$@"
hàm trong main1
(hoặc tạo một bản sao) và kiểm tra lại (hoặc cả hai main1
và bản sao main2
(với main2 "$@"
)) để so sánh. Đây là cấu trúc cơ bản phía dưới trong bài gốc (OP).
Nhưng tôi tự hỏi: tại sao cái vỏ lại mất nhiều thời gian để "không làm gì"?. Vâng, chỉ "một vài giây", nhưng vẫn vậy, tại sao?.
Điều này khiến tôi thử nghiệm trong các shell khác để phát hiện ra rằng chỉ có bash có vấn đề này.
Hãy thử ksh ./script
(kịch bản tương tự như trên).
Điều này dẫn đến mô tả này: gọi một hàm ( test#
) mà không có bất kỳ đối số nào bị trì hoãn bởi các đối số trong cha ( main#
). Đây là mô tả sau đây và là bài viết gốc (OP) bên dưới.
Bài gốc.
Gọi một hàm (trong Bash 4.4.12 (1) -release) để không làm gì f1(){ :; }
chậm hơn hàng ngàn lần so với :
nhưng chỉ khi có các đối số được xác định trong hàm gọi cha , Tại sao?
#!/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
Kết quả của 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
Không có đối số cũng như đầu vào hoặc đầu ra được sử dụng trong hàm f1
, độ trễ của hệ số một nghìn (1000) là không mong muốn. 1
Mở rộng các thử nghiệm đến một số vỏ, kết quả phù hợp, hầu hết các vỏ không gặp khó khăn cũng như không bị chậm trễ (sử dụng cùng n và 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
Các kết quả:
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 ......
Bỏ ghi chú hai bài kiểm tra khác để xác nhận rằng không seq
hoặc xử lý danh sách đối số là nguồn cho độ trễ.
1 Đượcbiết rằng việc truyền kết quả bằng các đối số sẽ tăng thời gian thực hiện. Cảm ơn@slm