2012-05-12 11 views
7

पर लूपिंग पर ओवरहेड मैं पाइथन के जेनरेटर और इटेरिएबल क्लास के साथ बस मस्ती के लिए झुका रहा था। असल में मैं कुछ ऐसा परीक्षण करना चाहता था जिसे मैंने कभी भी इस बारे में निश्चित नहीं किया है: कि पाइथन में कक्षाओं में कुछ महत्वपूर्ण ओवरहेड है और यदि आप कर सकते हैं तो एक वर्गर प्रोटोकॉल को लागू करने वाले वर्गों के बजाय yield लागू करने वाले तरीकों पर भरोसा करना बेहतर है।पायथन - एक पुनरावर्तक वर्ग

मैं गूगल में इस विषय पर एक संतोषजनक स्पष्टीकरण नहीं मिला, तो मैं इन दो सरल स्क्रिप्ट का उपयोग कर अपने दम पर उन्हें बाहर परीक्षण करने का फैसला: func_iter.py और class_iter.py

यहाँ func_iter.py है:

#!/usr/bin/env python 

import time 

x = 0 
def create_generator(num): 
    mylist = range(num) 
    for i in mylist: 
     yield i 

t = time.time() 
gen = create_generator(100000) 

for i in gen: 
    x = x + i 

print "%.3f" % (time.time() - t) 

और यहाँ class_iter.py है: थी का उपयोग कर 10 बार

#!/usr/bin/env python 

import time 

x = 0 

class Generator(object): 

    def __init__(self, num): 
     self.start = 0 
     self.end = num 

    def __iter__(self): 
     return self 

    def next(self): 
     if self.start == self.end: 
      raise StopIteration 
     else: 
      self.start = self.start + 1 
      return self.start 

t = time.time() 
gen = Generator(100000) 

for i in gen: 
    x = x + i 

print "%.3f" % (time.time() - t) 

मैं तो उनमें से प्रत्येक भाग गया बैश में (class_iter.py के लिए, उदाहरण के लिए):

for i in {1..10}; do ./class_iter.py; done 

और यहाँ उनमें से प्रत्येक के लिए औसत चल रहा समय है:

class_iter.py: 0.0864 
func_iter.py: 0.0307 

अब, मेरे सवाल कर रहे हैं:

  1. हैं मेरी विधियां सही हैं? क्या मेरी तुलना मेला है?
  2. यदि हां, तो बड़ा अंतर क्यों? class_iter.py को चलाने के लिए func_iter.py तक लगभग तीन बार क्यों लिया गया?
  3. यदि नहीं, तो मैं अपनी विधियों को कैसे सुधार सकता हूं या बेहतर तुलना के साथ आ सकता हूं?

संपादित करें: जैसा कि Dacav का सुझाव दिया, मैं भी xrangerange के बजाय का उपयोग कर func_iter.py चल कोशिश की। यह औसत औसत समय 0.0263 सेकंड तक घटता है।

उत्तर

5

कक्षा संस्करण अपने स्वयं के चरों तक पहुंचने में बहुत समय व्यतीत करता है। प्रत्येक self.whatever लागत चक्र।आप अपने __iter__ एक जनरेटर के रूप में परिभाषित करने और उदाहरण चर का उपयोग कम से कम करने के लिए, वर्ग और समारोह संस्करणों के बीच अंतर नगण्य हो जाएगा:

setup = """ 
def create_generator(num): 
    mylist = range(num) 
    for i in mylist: 
     yield i 

class Generator(object): 

    def __init__(self, num): 
     self.start = 0 
     self.end = num 

    def __iter__(self): 
     return self 

    def next(self): 
     if self.start == self.end: 
      raise StopIteration 
     else: 
      self.start = self.start + 1 
      return self.start 

class Generator2(object): 

    def __init__(self, num): 
     self.mylist = range(num) 

    def __iter__(self): 
     for i in self.mylist: 
      yield i 
""" 

import timeit 

print timeit.timeit('for p in create_generator(1000):p', setup, number=1000) 
print timeit.timeit('for p in Generator(1000):p', setup, number=1000) 
print timeit.timeit('for p in Generator2(1000):p', setup, number=1000) 

परिणाम:

0.158941984177 
0.696810007095 
0.160784959793 

तो दूसरा जनरेटर वर्ग लगभग है समारोह संस्करण के रूप में तेजी से।

कृपया ध्यान दें कि उदाहरण में Generator और Generator2 पूरी तरह से समतुल्य नहीं हैं, ऐसे मामले हैं जब आप जेनरेटर (उदा। मार्शलिंग) के साथ "सादा" इटरेटर को प्रतिस्थापित नहीं कर सकते हैं।

+0

मुझे नहीं लगता कि वह यही परीक्षण करना चाहता था। आप जेनरेटर की तुलना यहां जेनरेटर से कर रहे हैं, न कि इटरेटर प्रोटोकॉल के जनरेटर। हां, कक्षा अभी भी पुनरावर्तनीय है, लेकिन (उदाहरण के लिए) आप इसे राज्य नहीं चुन सकते हैं क्योंकि राज्य एक जनरेटर है जो कक्षा का सदस्य नहीं है। – agf

+0

पुष्टि की! यह अभी भी 0.002 सेकेंड के लिए धीमा है ~ क्या यह मानना ​​सुरक्षित है कि यह अंतर कक्षा के तत्काल होने के समय के कारण होता है? – bow

+0

@bow: हाँ, कक्षा तत्काल + '__iter__' में आवृत्ति चर का उपयोग। यदि आप दृश्यों के पीछे क्या चल रहा है यह देखने के लिए उत्सुक हैं, तो 'डी' मॉड्यूल आज़माएं। – georg

1

यदि आप अजगर का उपयोग कर रहे हैं तो अच्छे प्रदर्शन हैं कि आप सॉफ्टवेयर प्रदर्शन का लक्ष्य नहीं रख रहे हैं, लेकिन आप विकास में तेजी से और चुस्त होने के बारे में अधिक परवाह करते हैं।

ने कहा कि, मुझे लगता है कि तुलना विधि काफी उचित है जब तक आपका कोड एक समाधान के लिए पूर्वाग्रह से बचने के लिए पर्याप्त स्मार्ट नहीं है।

उदाहरण के लिए, yield-आधारित संस्करण के लिए एक संभावित सुधार range फ़ंक्शन विज्ञापन को हटाकर xrange फ़ंक्शन का उपयोग कर सकता है। अंतर (पायथन 2.x में) यह है कि range मूल्यों की एक सूची बनाता है (इसलिए इसे इसके लिए स्मृति में स्थान आवंटित करना होगा) जबकि xrange दिए गए मानों पर एक पुनरावृत्त वस्तु बनाता है।

+0

धन्यवाद! मैंने अभी यह कोशिश की है, और 'func_iter.py'' के लिए औसत समय अब ​​0.0263 तक घटता है। – bow

1

आप पूरी तरह से सही प्रतीत होते हैं और आपकी तुलना निष्पक्ष है। जब आप केवल ओवरहेड की तुलना करते हैं, तो इटरेटर प्रोटोकॉल का समर्थन करने वाला वर्ग जेनरेटर फ़ंक्शन से धीमा होगा।

हालांकि, असली दुनिया में, अगर कोड काफी जटिल है एक वर्ग का औचित्य साबित करने, एल्गोरिथ्म का चलने का समय भूमि के ऊपर बौना है, और इसलिए यह पूरी तरह से अपने कार्यक्रम के क्रम के लिए अप्रासंगिक हो जाएगा।

आप यहां माइक्रो-ऑप्टिमाइज़ेशन के बारे में चिंता कर रहे हैं। आपको नहीं करना चाहिए अच्छे, पठनीय कोड लिखने और नौकरी के लिए सही एल्गोरिदम का उपयोग करने पर ध्यान केंद्रित करें। वर्ग संस्करण में विशेषता लुकअप और विधि कॉल पर बिताए गए समय की आपकी बाधा नहीं होगी।

+0

आह :), मेरा इरादा वास्तव में उत्पादन कोड को अनुकूलित नहीं कर रहा था (हालांकि यह थोड़ा सा हो सकता है)। मैं उस चीज़ के बारे में उत्सुक था जो मैंने पहले सोचा था (लेकिन वास्तव में कभी साबित नहीं हुआ) ~ और मुझे यकीन है कि आप जानते हैं कि मिथक मिथक मजेदार है: डी। – bow

+0

@bow मैं कहने की कोशिश कर रहा हूं कि आप गलत सवाल पूछ रहे हैं। इससे कोई फर्क नहीं पड़ता कि गति अंतर क्या है। क्या मायने रखता है वह तरीका चुनना जो आपके कोड को बेहतर बनाता है। आप सही हैं कि कोई धीमा है, लेकिन गलत है कि आपको उस _at all_ के बारे में सोचना चाहिए। – agf

+0

@bow यह भी ध्यान देने योग्य है कि यह वास्तविक समस्याओं के लिए एक साइट है, सैद्धांतिक नहीं (एफएक्यू देखें), इसलिए आप कम से कम कुछ जवाब प्राप्त करने के लिए बाध्य हैं जो सवाल को संबोधित करते हैं जैसे कि यह सिर्फ अकादमिक नहीं था। – agf

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