11

मैं इस सिद्धांत के तहत परिचालन कर रहा हूं कि जनरेटर अभिव्यक्ति सामान्य लूप की तुलना में अधिक कुशल होती है। लेकिन फिर मैंने निम्नलिखित उदाहरण में भाग लिया: एक संख्या लिखें, N, और कुछ कारक, ps, N के तहत सभी संख्याओं का योग देता है जो कम से कम एक कारक हैं।यह जेनरेटर अभिव्यक्ति लूप संस्करण की तुलना में धीमी गति से क्यों काम करती है?

def loops(N, ps): 
    total_sum = 0 
    for i in xrange(N): 
     for p in ps: 
      if i%p == 0: 
       total_sum += i 
       break 
    return total_sum 

def genexp(N, ps): 
    return sum(i for i in xrange(N) 
       if any(i%p == 0 for p in ps)) 

मैं एक छोटे से तेजी से हो सकता है समझ संस्करण के साथ, लगभग बराबर प्रदर्शन करने के लिए दो उम्मीद थी, लेकिन मैं उम्मीद नहीं थी क्या:

यहाँ एक पाश संस्करण और एक छोटी जनरेटर अभिव्यक्ति संस्करण है यह था:

for func in ('loops', 'genexp'): 
    print func, timeit.timeit('%s(100000, [3,5,7])' % func, 
           number=100, 
           setup='from __main__ import %s' % func) 


loops 2.82878184319 
genexp 10.1663100719 

4x धीमी भी करीब नहीं है! क्यूं कर? मैं गलतफहमी क्या कर रहा हूं?

+0

आपके पास जनरेटर अभिव्यक्ति * है, सूची की समझ नहीं है। –

+0

@MartijnPieters धन्यवाद! स्पष्ट रूप से मैं एक अजगर आदमी नहीं हूँ :) – Barry

उत्तर

11

सबसे पहले: जेनरेटर अभिव्यक्ति मेमोरी कुशल, आवश्यक रूप से गति कुशल नहीं है।

आपका कॉम्पैक्ट genexp() संस्करण दो कारणों के लिए धीमी है:

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

  • sum() और any() नाम देखने के लिए अतिरिक्त ग्लोबल हैं। any() के मामले में, यह प्रति अतिरिक्त एन वैश्विक लुकअप है। ग्लोबल्स को एक शब्दकोश में देखा जाना चाहिए, बनाम स्थानीय जो सी-एरे में सूचकांक द्वारा देखे जाते हैं (जो बहुत तेज़ है)।

उत्तरार्द्ध एक छोटा सा घटक है, अधिकांश लागत फ्रेम (स्कॉप्स) बनाने और नष्ट करने में निहित है; यदि आप एक संस्करण बनाने जहां _any और _sum समारोह आपको मिल लेकिन प्रदर्शन में एक छोटा सा सुधार करने के लिए स्थानीय लोगों को कर रहे हैं:

>>> def genexp_locals(N, ps, _any=any, _sum=sum): 
...  return _sum(i for i in xrange(N) 
...    if _any(i%p == 0 for p in ps)) 
... 
>>> for func in ('loops', 'genexp', 'genexp_locals'): 
...  print func, timeit.timeit('%s(100000, [3,5,7])' % func, 
...        number=100, 
...        setup='from __main__ import %s' % func) 
... 
loops 2.00835800171 
genexp 6.45241594315 
genexp_locals 6.23843789101 

मैं एक स्थानीय xrange कि पहलू एक ही रखने के लिए के लिए का निर्माण नहीं किया। तकनीकी रूप से बोलते हुए, _any नाम जेनरेटर अभिव्यक्ति कोड ऑब्जेक्ट द्वारा एक स्थानीय के रूप में बंद नहीं किया जाता है, जो ग्लोबल लुकअप के रूप में धीमे नहीं होते हैं बल्कि स्थानीय लुकअप के रूप में तेज़ी से नहीं होते हैं।

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