2012-09-25 11 views
32

मुझे लगता है कि मैंने इसे गलत तरीके से कार्यान्वित किया होगा क्योंकि परिणाम समझ में नहीं आते हैं। मेरे पास एक प्रोग्राम है जो 1000000000वास्तव में पाइथन से बहुत तेज हो सकता है?

package main 

    import (
     "fmt" 
    ) 

    func main() { 
     for i := 0; i < 1000000000; i++ {} 
     fmt.Println("Done") 
    } 

यह एक सेकंड से भी कम समय में समाप्त होता है। दूसरी ओर मेरे पास एक पायथन लिपि

x = 0 
    while x < 1000000000: 
     x+=1 
    print 'Done' 

यह कुछ मिनटों में समाप्त होता है।

गो संस्करण इतना तेज़ क्यों है। क्या वे दोनों 1000000000 तक गिन रहे हैं या क्या मुझे कुछ याद आ रहा है?

उत्तर

66

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

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

-1

मैं जाने से परिचित नहीं हूँ, लेकिन मुझे लगता है कि चाहते हैं कि जाना संस्करण पाश के बाद से पाश के शरीर कुछ नहीं करता है पर ध्यान नहीं देता। दूसरी तरफ, पायथन संस्करण में, आप लूप के शरीर में x बढ़ा रहे हैं, इसलिए शायद यह वास्तव में लूप को निष्पादित कर रहा है।

+0

मैं प्रत्येक पाश के लिए एक और चर (अर्थात i2 = i) मैं आवंटित करने के लिए पाश के लिए बदल गया है और गति अभी भी वही था (ताकि मूल रूप से मुझे पता है पाश के लिए मार डाला जाता है)। – bab

+0

मेरे पास अंत में प्रोग्राम प्रिंट i2 था, और i2 99 99 99 999 – bab

15

इस परिदृश्य अत्यधिक सभ्य स्थानीय रूप संकलित स्थिर टाइप भाषाओं के पक्ष में होगा। मूल रूप से संकलित स्थैतिक-टाइप की गई भाषाएं कहने के लिए एक बहुत ही छोटे लूप को छोड़ने में सक्षम हैं, 4-6 सीपीयू ऑपकोड जो समाप्ति के लिए सरल चेक-हालत का उपयोग करती हैं। यह पाश को प्रभावी ढंग से शून्य शाखा भविष्यवाणी छूट जाए है और प्रभावी रूप से एक वेतन वृद्धि हर सीपीयू चक्र प्रदर्शन के बारे में सोचा जा सकता है (यह पूरी तरह सच नहीं है, लेकिन ..)

अजगर कार्यान्वयन काफी अधिक काम करना है , मुख्य रूप से गतिशील टाइपिंग के कारण। पायथन को दो int एस को जोड़ने के लिए कई अलग-अलग कॉल (आंतरिक और बाहरी) बनाना चाहिए। यह चाहिए अजगर में कॉल __add__ (यह प्रभावी रूप से i = i.__add__(1) है, लेकिन इस वाक्य रचना केवल अजगर 3.x में काम करेंगे), जो बारी में भेजे गए मान के प्रकार की जाँच करने के है (यह एक int है सुनिश्चित करने के लिए), तो यह पूर्णांक मूल्यों (उनमें वस्तुओं के दोनों से निकालने), और फिर नई पूर्णांक मान एक नई वस्तु में फिर से लपेटा जाता है कहते हैं। अंततः यह स्थानीय ऑब्जेक्ट पर नई ऑब्जेक्ट को फिर से असाइन करता है। यह बढ़ने के लिए एक ही ऑपोड की तुलना में अधिक महत्वपूर्ण काम करता है, और लूप को भी संबोधित नहीं करता है - तुलनात्मक रूप से, गो/मूल संस्करण संभवतः केवल साइड-इफेक्ट द्वारा एक रजिस्टर को बढ़ा रहा है।

जावा अधिक इस तरह के एक छोटे से बेंचमार्क में बेहतर होगा और संभवतः गो के करीब होगा; काउंटर वेरिएबल के जेआईटी और स्थैतिक-टाइपिंग यह सुनिश्चित कर सकते हैं (यह एक विशेष पूर्णांक जेवीएम निर्देश जोड़ता है)। एक बार फिर, पायथन का कोई फायदा नहीं है। अब, वहाँ PyPy/RPython की तरह कुछ कार्यान्वयन है, जो एक स्थिर टाइपिंग चरण चलाने के लिए और CPython यहाँ की तुलना में बेहतर पक्ष चाहिए .. हैं

+6

था, इसका मतलब यह नहीं था कि इसे बेंचमार्क के रूप में उपयोग करें (क्षमा करें अगर मैंने यह स्पष्ट नहीं किया है)। मैं बस सोच रहा था कि पाइथन संस्करण इतना धीमा क्यों था। – bab

+0

-1: आपकी अंतिम "स्वाभाविक रूप से भ्रामक" टिप्पणी औपचारिकता या स्पष्टीकरण के बिना एक फ्लैट दावे के रूप में अकेले खड़े होने लगती है। – igouy

+1

@igouy मुझे समझ में नहीं आता कि यह कैसे अनचाहे था (पूरी पोस्ट एक औचित्य थी), लेकिन मैंने इसे हटा दिया क्योंकि इसमें कुछ भी नया नहीं जोड़ा गया। –

-1

यह संकलक महसूस किया कि है कि आप के बाद "मैं" चर का उपयोग नहीं किया संभव है लूप, इसलिए यह लूप को हटाकर अंतिम कोड अनुकूलित किया गया।

यहां तक ​​कि अगर आप इसे बाद में उपयोग किया जाता है, संकलक शायद बहुत चालाक साथ

i = 1000000000; 

आशा इस = मदद करता है)

+0

आप यह जांच सकते हैं कि लूप अभी भी असेंबलर सूची प्राप्त करके कोड में है: 'build -gcflags -S main.go' – topskip

8

आप यहाँ काम पर दो बातें मिल गया है पाश विकल्प है। इनमें से पहला यह है कि गो को मशीन कोड में संकलित किया गया है और सीधे सीपीयू पर चलाया जाता है जबकि पायथन को बाइटकोड में एक (विशेष रूप से धीमी) वीएम के खिलाफ संकलित किया जाता है।

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

package main 

import (
    "fmt" 
) 

func main() { 
    for i := 0; i < 10; i++ { 
     fmt.Printf("%d %p\n", i, &i) 
    } 
} 

... और:

x = 0; 
while x < 10: 
    x += 1 
    print x, id(x) 

इसका कारण यह है जाओ, की वजह से यह सी जड़ें है, एक चर नाम एक जगह का उल्लेख करने लगते हैं, जहां पाइथन चीजों के संदर्भ में परिवर्तनीय नाम लेता है। चूंकि एक पूर्णांक को अजगर में एक अद्वितीय, अपरिवर्तनीय इकाई माना जाता है, इसलिए हमें लगातार नए बनाना चाहिए। पाइथन को गो से धीमा होना चाहिए, लेकिन आपने सबसे खराब स्थिति परिदृश्य चुना है - in the Benchmarks Game, हम औसतन 25x गुना तेज (सबसे खराब मामले में 100x) देखते हैं।

आपने शायद इसे पढ़ा है, यदि आपके पायथन प्रोग्राम बहुत धीमे हैं, तो आप चीजों को सी में ले जाकर उन्हें तेज कर सकते हैं। सौभाग्य से, इस मामले में, किसी ने पहले से ही यह आपके लिए किया है। आप अपने खाली पाश तो जैसे xrange() उपयोग करने के लिए फिर से लिखने हैं:

for x in xrange(1000000000): 
    pass 
print "Done." 

... आप इसके बारे में दो बार के रूप में तेजी से चलाने देखेंगे। यदि आपको अपने कार्यक्रम में वास्तव में एक बड़ी बाधा के लिए लूप काउंटर मिलते हैं, तो समस्या हल करने के नए तरीके की जांच करने का समय हो सकता है।

+1

पाइथन और गो - http: // shootout के बीच प्रत्यक्ष बेंचमार्क गेम तुलना का उपयोग करने के लिए बेहतर है। alioth.debian.org/u64q/benchmark.php?test=all&lang=go&lang2=python3 - आपको उलझन में लग रहा है और बताया कि पाइथन प्रदर्शन सी से तुलना करता है। – igouy

50

PyPy वास्तव में इस पाश

def main(): 
    x = 0 
    while x < 1000000000: 
     x+=1 

if __name__ == "__main__": 
    s=time.time() 
    main() 
    print time.time() - s 

$ python count.py 
44.221405983 
$ pypy count.py 
1.03511095047 

~ 97% speedup को तेज करने के एक प्रभावशाली काम करता है!

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

यह कहकर, कोड नमूने वास्तव में एक ही काम नहीं कर रहे हैं। पायथन को int ऑब्जेक्ट्स के 1000000000 को तुरंत चालू करने की आवश्यकता है। जाओ बस एक स्मृति स्थान बढ़ रहा है।

0

@troq

मैं पार्टी के लिए देर से एक छोटे से कर रहा हूँ, लेकिन मैं कहेंगे जवाब नहीं हाँ और है। जैसा कि @gnibbler ने इंगित किया है, सरल कार्यान्वयन में सीपीथन धीमा है लेकिन जब आपको इसकी आवश्यकता होती है तो बहुत तेज़ कोड के लिए पिट संकलित होता है।

आप CPython अधिकांश के साथ संख्यात्मक प्रसंस्करण कर रहे हैं NumPy सरणी और मैट्रिक्स पर तेजी से संचालन में जिसके परिणामस्वरूप के साथ यह करना होगा। हाल ही में मैं numba के साथ बहुत कुछ कर रहा हूं जो आपको अपने कोड में एक साधारण रैपर जोड़ने की अनुमति देता है। इसके लिए मैंने अभी @njit को फ़ंक्शन इंकलॉट() में जोड़ा है जो आपके कोड को ऊपर चलाता है।

मेरी मशीन पर CPython 61 सेकंड लेता है, लेकिन Numba आवरण के साथ यह 7.2 माइक्रोसेकंड जो सी के समान और शायद जाओ तुलना में तेजी से हो जाएगा ले जाता है। 8 मिलियन बार स्पीडअप है।

तो, पायथन में, अगर संख्या के साथ चीजों को थोड़ा धीमा लगता है, वहाँ उपकरण यह पता करने के लिए कर रहे हैं - और आप अभी भी पायथन के प्रोग्रामर उत्पादकता और आरईपीएल मिलता है।

def incALot(y): 
    x = 0 
    while x < y: 
     x += 1 

@njit('i8(i8)') 
def nbIncALot(y): 
    x = 0 
    while x < y: 
     x += 1 
    return x 

size = 1000000000 
start = time.time() 
incALot(size) 
t1 = time.time() - start 
start = time.time() 
x = nbIncALot(size) 
t2 = time.time() - start 
print('CPython3 takes %.3fs, Numba takes %.9fs' %(t1, t2)) 
print('Speedup is: %.1f' % (t1/t2)) 
print('Just Checking:', x) 

CPython3 takes 58.958s, Numba takes 0.000007153s 
Speedup is: 8242982.2 
Just Checking: 1000000000 
0

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

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