बैश को तर्क सूचियों का उपयोग करके परेशानी होती है?


11

बैश 5.0 में हल किया गया

पृष्ठभूमि

पृष्ठभूमि के लिए (और समझ (और इस सवाल को आकर्षित करने से बचने की कोशिश कर रहा है) को आकर्षित करने के लिए) मैं उस पथ को समझाऊंगा जो मुझे इस मुद्दे पर मिला (ठीक है, सबसे अच्छा मैं दो महीने बाद याद कर सकता हूं)।

मान लें कि आप यूनिकोड वर्णों की सूची के लिए कुछ शेल परीक्षण कर रहे हैं:

printf "$(printf '\\U%x ' {33..200})"

और 1 मिलियन से अधिक यूनिकोड वर्ण होने के कारण, उनमें से 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#)। यह वह वर्णन है जो निम्न था और नीचे मूल पोस्ट (ओपी) था।

मूल पोस्ट।

एक फ़ंक्शन को कॉल करना (बैश 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या तर्क सूची को संसाधित करना देरी के लिए स्रोत है।

1 यहज्ञात है कि तर्कों द्वारा परिणाम पारित करने से निष्पादन समय बढ़ जाएगा। धन्यवाद@ एसएलएम


3
मेटा प्रभाव द्वारा सहेजा गया। unix.meta.stackexchange.com/q/5021/3562
यहोशू

जवाबों:


9

से नकल: लूप में देरी क्यों? आपके निवेदन पर:

आप परीक्षण के मामले को छोटा कर सकते हैं:

time bash -c 'f(){ :;};for i do f; done' {0..10000}

यह एक फ़ंक्शन को बुला रहा है जबकि $@बड़ा है जो इसे ट्रिगर करने के लिए लगता है।

मेरा अनुमान है कि समय $@एक स्टैक पर बचत करने और बाद में इसे बहाल करने में खर्च होता है। संभवतः bashयह सभी मूल्यों या इस तरह की चीज़ों की नकल करके बहुत अकुशलता से करता है। समय o (n²) में लगता है।

आपको अन्य गोले में उसी तरह का समय मिलता है:

time zsh -c 'f(){ :;};for i do f "$@"; done' {0..10000}

यह वह जगह है जहाँ आप कार्यों के लिए तर्कों की सूची पास करते हैं, और इस बार शेल को मूल्यों की प्रतिलिपि बनाने की आवश्यकता है ( bashउस एक के लिए 5 गुना धीमी गति से समाप्त होता है)।

(मुझे शुरू में लगा कि यह बैश 5 (वर्तमान में अल्फा में) से भी बदतर है, लेकिन यह @gmont द्वारा बताए गए विकास संस्करणों में सक्षम होने वाले मॉलॉक डिबगिंग के नीचे था; यह भी जांचें कि bashयदि आप अपने स्वयं के निर्माण की तुलना करना चाहते हैं तो आपका वितरण कैसे बनता है। सिस्टम का एक। उदाहरण के लिए, उबंटू उपयोग करता है --without-bash-malloc)


डिबगिंग को कैसे हटाया जाता है?
इसहाक

@ आइसाक, मैंने इसे स्क्रिप्ट में बदलकर RELSTATUS=alphaकिया । RELSTATUS=releaseconfigure
स्टीफन चेज़लस

दोनों के लिए --without-bash-mallocऔर परीक्षा परिणाम के लिए जोड़ा गया परीक्षा RELSTATUS=releaseपरिणाम। कि अभी भी च के लिए कॉल के साथ एक समस्या दिखाते हैं।
इसहाक

@ इस्कैक, हां, मैंने सिर्फ यह कहा कि मैं यह कहना गलत था कि यह बैश 5 में खराब था। यह बुरा नहीं है, यह उतना ही बुरा है।
स्टीफन चेजलस

नहीं, यह उतना बुरा नहीं है । Bash5 कॉलिंग के साथ समस्या को हल करता है :और कॉल करने पर थोड़ा सुधार करता है f। प्रश्न में टेस्ट 2 समय को देखें।
इसहाक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.