2016-07-28 13 views
10

मैं पायथन जनरेटर की जांच कर रहा था और थोड़ा प्रयोग चलाने का फैसला किया।xrange द्वारा उत्पादित जनरेटर से उपज द्वारा उत्पादित जनरेटर क्यों उत्पन्न होता है?

TOTAL = 100000000 
def my_sequence(): 
    i = 0 
    while i < TOTAL: 
     yield i 
     i += 1 

def my_list(): 
    return range(TOTAL) 

def my_xrange(): 
    return xrange(TOTAL)  

मेमोरी उपयोग (psutil का उपयोग कर प्रक्रिया आरएसएस स्मृति पाने के लिए) और समय लिया (time.time() का प्रयोग करके) प्रत्येक विधि कई बार चल रहा है और औसत लेने के बाद नीचे दिए गए हैं:

sequence_of_values = my_sequence() # Memory usage: 6782976B Time taken: 9.53674e-07 s 

sequence_of_values2 = my_xrange() # Memory usage: 6774784B Time taken: 2.14576e-06 s 

list_of_values = my_list() # Memory usage: 3266207744B Time taken: 1.80253s 

मैंने देखा कि xrange का उपयोग करके जनरेटर का उत्पादन उपज का उपयोग करके लगातार (थोड़ा) धीमा होता है। ऐसा क्यों हैं?

+3

'xrange' एक अनुक्रम वस्तु जनरेटर नहीं है, इसलिए उनके आंतरिक बिल्कुल समान नहीं हैं। इसके अलावा, आपके द्वारा दिए गए समय वास्तव में xrange और जनरेटर के बीच काफी अंतर नहीं दिखाते हैं। असल में अंतर नगण्य है – smac89

+0

ठीक करने के लिए एक चीज वास्तव में सूची बनाने के लिए है जब आप 'xrange' का उपयोग करते हैं। 'My_xrange' फ़ंक्शन में, आप केवल xrange _generator_ लौट रहे हैं (यह वास्तव में जनरेटर नहीं है)। लेकिन अभी तक पूरी सूची में संसाधित नहीं किया गया है। तो यह उपरोक्त संख्याओं की तुलना में धीमी गति से भी जा सकता है। – aneroid

+2

आप केवल जनरेटर बनाने और 'xrange' ऑब्जेक्ट बनाने के लिए कितना समय लेते हैं - आप इन ऑब्जेक्ट्स पर वास्तव में पुन: प्रयास करने के लिए कितना समय लेते हैं ... – mgilson

उत्तर

9

मैं यह कहकर इस जवाब का प्रस्ताव देने जा रहा हूं कि इस पैमाने पर समय सटीक रूप से मापने के लिए कठिन हो सकता है (शायद timeit का उपयोग करने के लिए सबसे अच्छा) और यह कि अनुकूलन के इस प्रकार लगभग आपके वास्तविक में कोई फर्क नहीं पड़ेगा कार्यक्रम के क्रम ...

ठीक है, अब त्याग किया जाता है ...

पहली बात यह है कि आप सूचना के लिए की जरूरत है कि आप केवल जनरेटर/xrange वस्तु के निर्माण के समय कर रहे हैं - आप कर रहे हैं समय पर वास्तव में पुनरावृत्त करने में कितना समय लगता है । जानने के कुछ कारण हैं जिनकी वजह जनरेटर बनाने के लिए तेजी से xrange वस्तु बनाने की तुलना में कुछ मामलों में हो सकता है ...

  1. जनरेटर मामले के लिए कर रहे हैं, आप केवल एक जनरेटर बना रहे हैं - जनरेटर में कोई कोड वास्तव में हो जाता है चलाते हैं। यह लगभग 1 फ़ंक्शन कॉल की मात्रा है।
  2. xrange मामले के लिए, आप समारोह और तो आप है वैश्विक नाम xrange देखने के लिए वैश्विक TOTAL कॉल कर रहे हैं, और फिर आप कि builtin कॉल करने के लिए की जरूरत है - तो वहाँ अधिक चीजों में निष्पादित किया जा रहा हैं ये मामला।

स्मृति के लिए के रूप में - आलसी तरीकों में से दोनों में, प्रयुक्त स्मृति अजगर क्रम का बोलबाला हो जाएगा - नहीं अपने जनरेटर वस्तुओं के आकार के आधार पर। एकमात्र ऐसा मामला जहां आपकी स्क्रिप्ट द्वारा मेमोरी उपयोग की सराहना की जाती है वह मामला है जहां आप 100 मिलियन वस्तुओं की सूची बनाते हैं।

यह भी ध्यान रखें कि मैं वास्तव में अपने सिस्टम पर लगातार अपने परिणामों की पुष्टि नहीं कर सकते हैं ... timeit का उपयोग करना, मैं वास्तव में है कि my_xrangeकभी कभी तेजी से (~ 30%) के निर्माण के लिए है मिलता है।

अपनी स्क्रिप्ट के नीचे करने के लिए निम्न जोड़ना:

from timeit import timeit 
print timeit('my_xrange()', setup='from __main__ import my_xrange') 
print timeit('my_sequence()', setup='from __main__ import my_sequence') 

और मेरे परिणाम (पर ओएस एक्स एल Capitan CPython के लिए) इस प्रकार हैं:

0.227491140366 
0.356791973114 

हालांकि, pypy के पक्ष में रहा है जनरेटर निर्माण (मैंने इसे पहले my_xrange दोनों और my_sequence दोनों के साथ आजमाया और काफी लगातार परिणाम प्राप्त किए, हालांकि पहले व्यक्ति को चलाने के लिए कुछ नुकसान होता है - शायद जेआईटी गर्म समय के कारण या तो mething):

0.00285911560059 
0.00137305259705 

यहाँ, मैं xrange बढ़त हासिल है की उम्मीद करेंगे - लेकिन फिर, कुछ भी सच है जब तक आप timeit और फिर यह केवल सच है अगर समय मतभेद हैं महत्वपूर्ण और यह केवल कंप्यूटर पर सच है जहां आपने समय किया था।
देखें उद्घाटन अस्वीकरण :-P

+0

और इसके अलावा, पहले दो मामलों में सूचीबद्ध "कुल मेमोरी" वास्तव में पाइथन रनटाइम द्वारा उपयोग की जाने वाली मेमोरी है। – jsbueno

+1

@jsbueno - ओह हाँ, मैं उस बिट को पूरी तरह से अनदेखा कर रहा हूं। यह 'my_list' केस द्वारा सभी मामलों के लिए अप्रासंगिक है :-) – mgilson

3

जैसा कि मैंने ऊपर मेरी टिप्पणी में उल्लेख किया है, अपने जनरेटर समारोह के साथ और xrange साथ, आप वास्तव में अनुक्रम नहीं बना रहे, केवल वस्तु का निर्माण। @ mgilson के उत्तर में से संबंधित कॉल शामिल हैं जो बनाते हैं।

वास्तव में उनके साथ कुछ करने के लिए के रूप में:

>>> TOTAL = 100000 
>>> # your functions here 
... 
>>> import timeit 
>>> timeit.timeit("list(my_seq())", setup="from __main__ import my_seq", number=1000) 
9.783777457339898 
>>> timeit.timeit("list(my_xrange())", setup="from __main__ import my_xrange", number=1000) 
1.2652621698083024 
>>> timeit.timeit("list(my_list())", setup="from __main__ import my_list", number=1000) 
2.666709824464867 
>>> timeit.timeit("my_list()", setup="from __main__ import my_list", number=1000) 
1.2324339537661615 
  1. आपको लगता है कि मैं एक list प्रत्येक से बाहर बना रहा हूं तो मैं दृश्यों प्रसंस्करण कर रहा हूँ देखेंगे।

  2. जनरेटर फ़ंक्शन xrange के लिए लगभग 10x समय है। के बाद से पहले से ही my_list सूची range द्वारा उत्पादित रिटर्न

  3. list(my_list) बेमानी है, इसलिए मैं list() करने के लिए कॉल के बिना एक बार ऐसा किया।

  4. range लगभग xrange रूप में ही है, लेकिन है कि क्योंकि मैं कुल कम है। सबसे बड़ा अंतर यह होगा कि range अधिक स्मृति का उपभोग करेगा क्योंकि यह पूरी सूची बनाता है और इसलिए केवल उस भाग में लेता है। प्रभावी ढंग से xrange = रेंज से एक सूची बनाना। तो उपयोग की जाने वाली अंतिम स्मृति वही होगी और चूंकि मैं केवल xrange से एक सूची बना रहा हूं, इस छोटे मामले में अंतर देखना मुश्किल है।

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