python - एक समारोह में पाइथन कोड तेजी से क्यों चलता है?




performance profiling (2)

आप पूछ सकते हैं कि ग्लोबल्स की तुलना में स्थानीय चरों को स्टोर करना क्यों तेज़ है। यह एक सीपीथन कार्यान्वयन विस्तार है।

याद रखें कि सीपीथन को बाइटकोड में संकलित किया गया है, जो दुभाषिया चलता है। जब कोई फ़ंक्शन संकलित किया जाता है, तो स्थानीय चर एक निश्चित आकार सरणी (एक dict नहीं ) में संग्रहीत होते हैं और परिवर्तनीय नाम इंडेक्स को आवंटित किए जाते हैं। यह संभव है क्योंकि आप किसी फ़ंक्शन में गतिशील रूप से स्थानीय चर जोड़ नहीं सकते हैं। फिर स्थानीय चर को पुनर्प्राप्त करना सचमुच सूची में एक सूचक खोज है और PyObject पर एक PyObject वृद्धि जो तुच्छ है।

इसे वैश्विक लुकअप ( LOAD_GLOBAL ) पर तुलना करें, जो हैश से जुड़ी एक सच्ची LOAD_GLOBAL खोज है और इसी तरह। संयोग से, यही कारण है कि आपको global i से निर्दिष्ट करने की आवश्यकता है यदि आप इसे वैश्विक होना चाहते हैं: यदि आपने कभी भी एक दायरे के अंदर एक चर को असाइन किया है, तो संकलक STORE_FAST एस को तब तक पहुंच देगा जब तक कि आप इसे न कहें।

वैसे, वैश्विक लुकअप अभी भी बहुत अनुकूलित हैं। विशेषता लुकअप foo.bar वास्तव में धीमी हैं!

यहां स्थानीय परिवर्तनीय दक्षता पर छोटा illustration गया है।

def main():
    for i in xrange(10**8):
        pass
main()

पायथन में कोड का यह टुकड़ा चलता है (नोट: समय लिनक्स में बीएएसएच में समय समारोह के साथ किया जाता है।)

real    0m1.841s
user    0m1.828s
sys     0m0.012s

हालांकि, अगर फ़ंक्शन के लिए लूप को नहीं रखा गया है,

for i in xrange(10**8):
    pass

तो यह बहुत अधिक समय तक चलता है:

real    0m4.543s
user    0m4.524s
sys     0m0.012s

ऐसा क्यों है?


एक समारोह के अंदर, बाइटकोड है

  2           0 SETUP_LOOP              20 (to 23)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_FAST               0 (i)

  3          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               0 (None)
             26 RETURN_VALUE        

शीर्ष स्तर पर, बाइटकोड है

  1           0 SETUP_LOOP              20 (to 23)
              3 LOAD_NAME                0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_NAME               1 (i)

  2          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               2 (None)
             26 RETURN_VALUE        

अंतर यह है कि STORE_FAST STORE_NAME तुलना में तेज़ (!) है। ऐसा इसलिए है क्योंकि एक समारोह में, i एक स्थानीय i लेकिन पर्याप्त रूप से यह एक वैश्विक है।

बाइटकोड की जांच करने के लिए, डी मॉड्यूल का उपयोग करें। मैं सीधे फ़ंक्शन को अलग करने में सक्षम था, लेकिन अपूर्ण कोड को अलग करने के लिए मुझे compile निर्मित का उपयोग करना पड़ा।







cpython