2009-09-08 18 views
33

मैं कुछ अजगर कोड के अनुकूलन किया गया था, और करने की कोशिश की निम्न प्रयोग:पाइथन में अतिरिक्त से अधिक घटाव क्यों तेज है?

import time 

start = time.clock() 
x = 0 
for i in range(10000000): 
    x += 1 
end = time.clock() 

print '+=',end-start 

start = time.clock() 
x = 0 
for i in range(10000000): 
    x -= -1 
end = time.clock() 

print '-=',end-start 

दूसरा पाश मज़बूती से तेजी से होता है, मामूली से कहीं भी 10% तक, प्रणाली मैं इस पर चलाने के आधार पर। मैंने लूप्स, निष्पादन की संख्या इत्यादि के क्रम को बदलने की कोशिश की है, और यह अभी भी काम करता प्रतीत होता है। जब पाश सामग्री समान हैं

अजनबी,

for i in range(10000000, 0, -1): 

(यानी पाश पीछे की ओर चल) से अधिक तेजी से

for i in range(10000000): 

भी है।

क्या देता है, और यहां एक और सामान्य प्रोग्रामिंग सबक है?

+4

क्या आपने 'रेंज()' के बजाय 'xrange() 'के साथ यह कोशिश की है, इसलिए पाइथन को दस मिलियन पूर्णांक के लिए स्थान आवंटित करने की आवश्यकता नहीं है? साथ ही, क्या आप इसे पायथन 2.x या पायथन 3.x पर चला रहे हैं, जिसमें 'श्रेणी()' के महत्वपूर्ण कार्यान्वयन हैं? –

+0

क्या यह संभव है कि आप इस कोड को चर आवृत्ति वाले CPU पर चलाएं? – tzot

उत्तर

13
$ python -m timeit -s "x=0" "x+=1" 
10000000 loops, best of 3: 0.151 usec per loop 
$ python -m timeit -s "x=0" "x-=-1" 
10000000 loops, best of 3: 0.154 usec per loop 

ऐसा लगता है कि कुछ measurement bias

+0

बहुत दिलचस्प लेख। – hirschhornsalz

+1

निश्चित रूप से मापन पूर्वाग्रह का बिंदु यह है कि आपका एक प्रति उदाहरण यह एक या दूसरे साबित नहीं करता है। तो मैं _may_ कुछ माप पूर्वाग्रह है! – Statto

+0

यद्यपि आपका थोड़ा धीमा हो गया .. ..) – Pod

-1

चल पाश पीछे की ओर तेजी से कर रहा है मैं "सामान्य प्रोग्रामिंग लगता है कि अगर एक नंबर 0.

+1

हालांकि यह अनुकूलित असेंबली भाषा में सच हो सकता है, यह आमतौर पर पायथन के रूप में उच्च स्तर की भाषाओं के लिए सत्य नहीं है, और किसी भी मामले में 10% के रूप में महत्वपूर्ण अंतर बनाने की संभावना नहीं है। –

+0

यह x मान पर कोई तुलना नहीं कर रहा है? – Pod

+0

न तो लूप पीछे की ओर चल रहा है। वे दोनों ऊपर जा रहे हैं, एक जोड़ 1 और एक घटाना -1। –

7

के बराबर है क्योंकि कंप्यूटर एक आसान समय की तुलना है है पाठ "यह है कि यह है वास्तव में भविष्यवाणी करने के लिए मुश्किल है, केवल स्रोत कोड को देखकर, जो कथन का अनुक्रम सबसे तेज़ होगा। सभी स्तरों पर प्रोग्रामर अक्सर इस तरह के "अंतर्ज्ञानी" अनुकूलन से पकड़े जाते हैं। जो आपको लगता है कि आपको पता है कि यह सच नहीं हो सकता है।

वास्तव में मापने के लिए कोई विकल्प नहीं है आपके प्रोग्राम प्रदर्शन को मापता है। ऐसा करने के लिए Kudos; का जवाब क्यों निस्संदेह इस मामले में पाइथन के कार्यान्वयन में गहराई से जलने की आवश्यकता है।

जावा, पायथन और .NET जैसे बाइट-संकलित भाषाओं के साथ, यह केवल एक मशीन पर प्रदर्शन को मापने के लिए पर्याप्त नहीं है। वीएम संस्करणों, देशी कोड अनुवाद कार्यान्वयन, सीपीयू-विशिष्ट अनुकूलन, और इसी तरह के बीच मतभेद इस तरह के प्रश्न को उत्तर देने के लिए और अधिक कठिन बना देंगे।

0

पायथन 2.5 के साथ यहां सबसे बड़ी समस्या सीमा का उपयोग कर रही है, जो उस सूची को फिर से चालू करने के लिए बड़ी सूची आवंटित करेगी। Xrange का उपयोग करते समय, जो भी दूसरा किया जाता है वह मेरे लिए थोड़ा तेज़ होता है। (सुनिश्चित नहीं है कि रेंज पाइथन 3 में जनरेटर बन गया है।)

+0

मैंने xrange (2.6.2 में) के साथ परीक्षण किया, और = = 1 अभी भी + = 1 की तुलना में मेरे लिए लगातार तेज है, भले ही पहले कोई किया जाता है। –

4

यह हमेशा एक अच्छा विचार है कि एक मंच पूछने के लिए कि कौन सा मंच और पाइथन का संस्करण आप उपयोग कर रहे हैं। कभी-कभी इससे कोई फर्क नहीं पड़ता। यह उन समयों में से एक नहीं है:

  1. time.clock() केवल विंडोज़ पर उपयुक्त है। अपने स्वयं के मापने वाले कोड को फेंक दें और -m timeit का उपयोग करें जैसा कि पिक्सेलबीट के उत्तर में दिखाया गया है।

  2. पायथन 2.X का range() एक सूची बनाता है। यदि आप पायथन 2.x का उपयोग कर रहे हैं, rangexrange के साथ बदलें और देखें कि क्या होता है।

  3. पायथन 3.X का int Python2.X का long है।

72

मैं इसे अपने Q6600 (पायथन 2.6.2) पर पुन: उत्पन्न कर सकता हूं; 100000000 के लिए रेंज बढ़ाने:

('+=', 11.370000000000001) 
('-=', 10.769999999999998) 

सबसे पहले, कुछ टिप्पणियों:

  • यह एक तुच्छ ऑपरेशन के लिए 5% है। यह महत्वपूर्ण है।
  • देशी जोड़ और घटाव opcodes की गति अप्रासंगिक है। यह शोर तल में है, बाइटकोड मूल्यांकन द्वारा पूरी तरह से बौना। यह हजारों के आसपास एक या दो मूल निर्देशों के बारे में बात कर रहा है।
  • बाइटकोड बिल्कुल समान संख्या में निर्देश उत्पन्न करता है; केवल अंतर INPLACE_ADD बनाम INPLACE_SUBTRACT और +1 बनाम -1 है।

पायथन स्रोत को देखते हुए, मैं अनुमान लगा सकता हूं। इसे PyEval_EvalFrameEx में, ceval.c में संभाला जाता है। स्ट्रिंग concatenation को संभालने के लिए INPLACE_ADD कोड का एक महत्वपूर्ण अतिरिक्त ब्लॉक है। वह ब्लॉक INPLACE_SUBTRACT में मौजूद नहीं है, क्योंकि आप तारों को घटा नहीं सकते हैं। इसका अर्थ है INPLACE_ADD में अधिक मूल कोड शामिल है। कंपाइलर द्वारा कोड कैसे उत्पन्न किया जा रहा है, इस पर निर्भर करता है (भारी!), यह अतिरिक्त कोड INPLACE_ADD कोड के साथ इनलाइन हो सकता है, जिसका अर्थ है कि जोड़ों को घटाव की तुलना में निर्देश कैश को कड़ी टक्कर मार सकती है। यह अतिरिक्त एल 2 कैश हिट पैदा कर सकता है, जो एक महत्वपूर्ण प्रदर्शन अंतर पैदा कर सकता है।

यह उस प्रणाली पर निर्भर है जिस पर आप हैं (विभिन्न प्रोसेसर के पास कैश और कैश आर्किटेक्चर की अलग-अलग मात्रा होती है), विशेष संस्करण और संकलन विकल्पों सहित कंपाइलर (विभिन्न कंपाइलर्स अलग-अलग तय करेंगे कि कोड के बिट्स महत्वपूर्ण पथ पर हैं, जो यह निर्धारित करता है कि कैसे असेंबली कोड एक साथ लम्बा हुआ है), और इसी तरह।

इसके अलावा, अंतर पाइथन 3.0.1 (+: 15.66, -: 16.71) में उलट दिया गया है; इसमें कोई संदेह नहीं है कि इस महत्वपूर्ण कार्य में काफी बदलाव आया है।

+0

काफी प्रबुद्ध। +1 – Triptych

+5

अरे, यह वास्तव में अच्छा जवाब नहीं दिया गया है :( – mgiuca

+0

+1 महान काम ग्लेन – alex

0

आपका प्रयोग दोषपूर्ण है। इस प्रयोग को डिजाइन करने के तरीके को 2 अलग-अलग कार्यक्रम लिखना है - 1 अतिरिक्त के लिए, घटाव के लिए 1। वे बिल्कुल वही होना चाहिए और उसी स्थिति के तहत चलाना चाहिए जिसमें डेटा डाला जा रहा है। फिर आपको रनों (कम से कम कई हजार) औसत करने की आवश्यकता है, लेकिन आपको एक उचित संख्या बताने के लिए आपको एक सांख्यिकीविद् की आवश्यकता होगी।

यदि आप अतिरिक्त, विधियों और लूपिंग के विभिन्न तरीकों का विश्लेषण करना चाहते हैं, तो उनमें से प्रत्येक एक अलग कार्यक्रम होना चाहिए।

प्रायोगिक त्रुटि प्रोसेसर और अन्य गतिविधि cpu पर जाने की गर्मी से पैदा हो सकता है, तो मैं पैटर्न की एक किस्म में रन पर अमल करेंगे ...

+0

उसे चाहिए, लेकिन यह समस्या नहीं है। मैंने इसे दोनों तरीकों से परीक्षण किया है, और मुझे वही परिणाम मिलते हैं; घटाव मामला 2.6 में मेरे लिए लगातार तेज है। –

5

"दूसरी पाश मज़बूती से तेजी से होता है .. "

यह आपकी व्याख्या है। फिर से आदेश अपनी स्क्रिप्ट तो घटाव परीक्षण पहले का समय समाप्त हो जाता है, तो इसके अलावा, और अचानक इसके अलावा तेजी से ऑपरेशन फिर से हो जाता है:

-= 3.05 
+= 2.84 

जाहिर है कुछ स्क्रिप्ट यह है कि तेजी से की दूसरी छमाही के लिए होता है।मेरे अनुमान कि range() करने के लिए पहली कॉल धीमी है क्योंकि अजगर इस तरह के एक लंबी सूची के लिए पर्याप्त स्मृति को आबंटित करने की जरूरत है, लेकिन यह range() को दूसरी कॉल के लिए है कि स्मृति का फिर से उपयोग करने में सक्षम है:

import time 
start = time.clock() 
x = range(10000000) 
end = time.clock() 
del x 
print 'first range()',end-start 
start = time.clock() 
x = range(10000000) 
end = time.clock() 
print 'second range()',end-start 

इस स्क्रिप्ट के कुछ रन पता चलता है कि अतिरिक्त समय के लगभग सभी के बीच समय अंतर का के लिए पहली range() खातों के लिए आवश्यक '+ =' और '- =' ऊपर देखी गई:

first range() 0.4 
second range() 0.23 
+0

dtmunir के उत्तर की डुप्पी; ओपी ने पहले ही कहा है कि उसे आदेश के बावजूद एक ही परिणाम मिलते हैं। मैंने इसे पुन: उत्पन्न किया है, साथ ही साथ जब दो परीक्षण स्वतंत्र रूप से चलते हैं, और जब श्रेणी के बजाय xrange का उपयोग किया जाता है। –

+0

@Glenn: मैंने व्याख्या की है कि 'लूप के क्रम को अलग-अलग करने की कोशिश की गई है' श्रेणी (10000000, 0, -1) के बजाय रेंज (10000000, 0, -1) 'के रूप में, क्योंकि वह तुरंत उपयोग करने के बारे में अधिक जानकारी देता है उलटा रेंज()। वह विशेष रूप से नहीं कहता है कि उसने पहले घटाव परीक्षण करने की कोशिश की थी। डीटीएमनीर के जवाब में एक ही अस्पष्टता होती है, शायद यही कारण है कि इसे डाउनवॉटेड किया गया था। –

+0

मैं उल्टा सीमा का उपयोग करने के बारे में विस्तार से 'तुरंत' नहीं जाता! मैंने पहले घटाव लूप डालने का प्रयास किया था, और कई ऑर्डरों में कई बार परीक्षण भी चलाया था। – Statto

0

कि उल्लेखनीय है, तो हो सकता है मैंने आपके कोड का पूरी तरह से मूल्यांकन किया है और विस्तार के रूप में विस्तार भी स्थापित किया है डी इसे और अधिक देखें सही (लूप के बाहर सभी घोषणाएं और फ़ंक्शन कॉल)। दोनों संस्करणों में मैंने पांच बार भाग लिया है।

  • अपने कोड को चलाने से आपके दावों को सत्यापित किया गया: - = लगातार कम समय लेता है; 3.6% औसत
  • मेरा कोड चलाना, हालांकि, आपके प्रयोग के नतीजे का विरोध करता है: + = औसतन (हमेशा नहीं) 0.5% कम समय लेता है।

मैं भूखंडों डाल दिया है सभी परिणाम दिखाने के लिए ऑनलाइन:

तो, मैं निष्कर्ष है अपने प्रयोग एक पूर्वाग्रह है कि, और यह महत्वपूर्ण है।

अंत में यहाँ मेरी कोड है:

import time 

addtimes = [0.] * 100 
subtracttimes = [0.] * 100 

range100 = range(100) 
range10000000 = range(10000000) 

j = 0 
i = 0 
x = 0 
start = 0. 


for j in range100: 
start = time.clock() 
x = 0 
for i in range10000000: 
    x += 1 
addtimes[j] = time.clock() - start 

for j in range100: 
start = time.clock() 
x = 0 
for i in range10000000: 
    x -= -1 
subtracttimes[j] = time.clock() - start 

print '+=', sum(addtimes) 
print '-=', sum(subtracttimes) 
2
वहाँ एक अधिक सामान्य प्रोग्रामिंग सबक यहाँ है?

यहां अधिक सामान्य प्रोग्रामिंग सबक यह है कि कंप्यूटर कोड के रन-टाइम प्रदर्शन की भविष्यवाणी करते समय अंतर्ज्ञान एक खराब मार्गदर्शिका है।

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

संबंधित मुद्दे