दिलचस्प पोस्ट
JSON ऑब्जेक्ट्स पर __प्रकार संपत्ति को क्रमबद्ध नहीं कैसे करें एक तारीख को वर्ष और महीने ("य्या-मिमी" प्रारूप) परिवर्तित करना? जीआईटी-एलएस-ट्री के आउटपुट के मोड फ़ील्ड को कैसे पढ़ा जाए मैं PHP में कैसे एक UTF-8 सीएसवी आउटपुट कर सकता हूं जो Excel ठीक से पढ़ा जाएगा? मैं "कंपाइलर विर्सन" आईआईएस त्रुटि कैसे ठीक करूं? बैच: उत्पादन को पार्स करने के दौरान चर ओवरराइटिंग दुर्व्यवहार को कैसे ठीक करें PHP में सरणी के लिए ऑपरेटर? अज्ञात कॉलम की संख्या को कैसे धुएं और SQL सर्वर में कुल मिलाकर कैसे करें? अगर एक MySQL क्वेरी में ELSE कथन I डेटाबेस सामान्य रूप क्या हैं, आप उन्हें आसानी से कैसे लागू कर सकते हैं और आप उदाहरण दे सकते हैं? ब्राउजर को इसे खोलने के बजाय पीडीएफ दस्तावेज़ डाउनलोड करने के लिए मजबूर करें JavaScript >>> ऑपरेटर क्या है और आप इसे कैसे उपयोग करते हैं? यदि चयनित हो तो ListBox आइटम के लिए WPF DataTemplate बदलें Django वर्ग-आधारित दृश्य: मैं as_view विधि में अतिरिक्त पैरामीटर कैसे पारित करूं? एमवीसी एजेक्स जेसन पोस्ट नियंत्रक कार्रवाई विधि के लिए

सूची समझ जनरेटर अभिव्यक्ति के अजीब timeit परिणाम बनाम?

मैं इस प्रश्न का उत्तर दे रहा था, मैं यहां जनरेटर अभिव्यक्ति को पसंद करता था और इसका इस्तेमाल किया था, जिसे मैंने सोचा था कि जितना तेज़ होगा उतना जनरेटर को पूरी सूची बनाने की आवश्यकता नहीं है:

>>> lis=[['a','b','c'],['d','e','f']] >>> 'd' in (y for x in lis for y in x) True 

और लेवोन ने अपने समाधान में सूची की समझ को इस्तेमाल किया,

 >>> lis = [['a','b','c'],['d','e','f']] >>> 'd' in [j for i in mylist for j in i] True 

लेकिन जब मैंने इन एलसी के लिए टाइमिट परिणाम किया तो जनरेटर की तुलना में तेज़ था:

 ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f']]" "'d' in (y for x in lis for y in x)" 100000 loops, best of 3: 2.36 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f']]" "'d' in [y for x in lis for y in x]" 100000 loops, best of 3: 1.51 usec per loop 

तब मैंने सूची के आकार में वृद्धि की और इसे फिर से समाप्त किया:

 lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]] 

'd' जनरेटर की खोज के लिए इस बार एलसी की तुलना में तेज़ था, लेकिन जब मैंने एक मध्यम तत्व (11) और अंतिम तत्व की खोज की तो एलसी फिर जनरेटर अभिव्यक्ति को धड़क रहा है, और मुझे क्यों नहीं समझा जा सकता?

 ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "'d' in (y for x in lis for y in x)" 100000 loops, best of 3: 2.96 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "'d' in [y for x in lis for y in x]" 100000 loops, best of 3: 7.4 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "11 in [y for x in lis for y in x]" 100000 loops, best of 3: 5.61 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "11 in (y for x in lis for y in x)" 100000 loops, best of 3: 9.76 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "18 in (y for x in lis for y in x)" 100000 loops, best of 3: 8.94 usec per loop ~$ python -m timeit -s "lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18]]" "18 in [y for x in lis for y in x]" 100000 loops, best of 3: 7.13 usec per loop 

Solutions Collecting From Web of "सूची समझ जनरेटर अभिव्यक्ति के अजीब timeit परिणाम बनाम?"

पाउलो के जवाब पर विस्तार, जनरेटर अभिव्यक्ति अक्सर कॉल की तुलना में धीमी होती है क्योंकि फ़ंक्शन कॉल्स की ओवरहेड होती है। इस मामले में, यदि आइटम काफी जल्दी मिल गया है, लेकिन अन्यथा, पैटर्न धारण करता है तो धीमा in ऑफ़सेट in शॉर्ट-सर्किटिंग व्यवहार।

मैं अधिक विस्तृत विश्लेषण के लिए प्रोफाइलर के माध्यम से एक सरल स्क्रिप्ट चलाया यहां स्क्रिप्ट है:

 lis=[['a','b','c'],['d','e','f'],[1,2,3],[4,5,6], [7,8,9],[10,11,12],[13,14,15],[16,17,18]] def ge_d(): return 'd' in (y for x in lis for y in x) def lc_d(): return 'd' in [y for x in lis for y in x] def ge_11(): return 11 in (y for x in lis for y in x) def lc_11(): return 11 in [y for x in lis for y in x] def ge_18(): return 18 in (y for x in lis for y in x) def lc_18(): return 18 in [y for x in lis for y in x] for i in xrange(100000): ge_d() lc_d() ge_11() lc_11() ge_18() lc_18() 

यहां दिए गए प्रासंगिक परिणाम हैं, पैटर्न को साफ करने के लिए पुनः क्रमबद्ध

  5400002 function calls in 2.830 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 100000 0.158 0.000 0.251 0.000 fop.py:3(ge_d) 500000 0.092 0.000 0.092 0.000 fop.py:4(<genexpr>) 100000 0.285 0.000 0.285 0.000 fop.py:5(lc_d) 100000 0.356 0.000 0.634 0.000 fop.py:8(ge_11) 1800000 0.278 0.000 0.278 0.000 fop.py:9(<genexpr>) 100000 0.333 0.000 0.333 0.000 fop.py:10(lc_11) 100000 0.435 0.000 0.806 0.000 fop.py:13(ge_18) 2500000 0.371 0.000 0.371 0.000 fop.py:14(<genexpr>) 100000 0.344 0.000 0.344 0.000 fop.py:15(lc_18) 

एक जनरेटर अभिव्यक्ति बनाना जनरेटर फ़ंक्शन बनाने और इसे कॉल करने के बराबर है । यह एक कॉल <genexpr> लिए कॉल करता है फिर, पहले मामले में, next 4 बार कहा जाता है, जब तक d तक नहीं पहुंच जाता है, कुल 5 कॉलों (100000 रेखांकन = एनकॉल = 500000) के लिए। दूसरे मामले में, कुल 18 कॉलों के लिए इसे 17 बार कहा जाता है; और तीसरे में, 24 बार, कुल 25 कॉलों के लिए

जीनेक्स ने पहले मामले में सूची की समझ को मात दे दिया था, लेकिन दूसरे और तीसरे मामलों में सूची की समझ की गति और जनरेटर अभिव्यक्ति की गति के बीच अधिकांश अंतर के लिए next खाते में अतिरिक्त कॉल।

 >>> .634 - .278 - .333 0.023 >>> .806 - .371 - .344 0.091 

मुझे यकीन नहीं है कि शेष समय के लिए क्या खाता है; ऐसा लगता है कि जनरेटर अभिव्यक्ति अतिरिक्त फ़ंक्शन कॉल के बिना भी एक बाल धीमी होगी। मुझे लगता है कि यह निरीक्षक G4dget के इस तर्क की पुष्टि करता है कि "एक जनरेटर समझ बनाने के लिए सूची की समझ से अधिक मूल निवासी है।" लेकिन किसी भी मामले में, यह बहुत स्पष्ट रूप से दिखाता है कि जनरेटर अभिव्यक्तियां next कारणों के कारण अधिक धीमी होती हैं।

मैं जोड़ दूँगा कि जब शॉर्ट सर्किट मदद नहीं करता है, तो सूची की गहराई अभी भी तेज है, यहां तक ​​कि बहुत बड़ी सूचियों के लिए भी। उदाहरण के लिए:

 >>> counter = itertools.count() >>> lol = [[counter.next(), counter.next(), counter.next()] for _ in range(1000000)] >>> 2999999 in (i for sublist in lol for i in sublist) True >>> 3000000 in (i for sublist in lol for i in sublist) False >>> %timeit 2999999 in [i for sublist in lol for i in sublist] 1 loops, best of 3: 312 ms per loop >>> %timeit 2999999 in (i for sublist in lol for i in sublist) 1 loops, best of 3: 351 ms per loop >>> %timeit any([2999999 in sublist for sublist in lol]) 10 loops, best of 3: 161 ms per loop >>> %timeit any(2999999 in sublist for sublist in lol) 10 loops, best of 3: 163 ms per loop >>> %timeit for i in [2999999 in sublist for sublist in lol]: pass 1 loops, best of 3: 171 ms per loop >>> %timeit for i in (2999999 in sublist for sublist in lol): pass 1 loops, best of 3: 183 ms per loop 

जैसा कि आप देख सकते हैं, जब शॉर्ट सर्किट अप्रासंगिक है, सूचि की सूची में एक मिलियन-मद-लंबी सूची के लिए सूची की गणना लगातार तेज होती है। स्पष्ट रूप से इन तराजू in वास्तविक उपयोग के लिए, शॉर्ट सर्किटिंग के कारण जनरेटर तेजी से हो जाएगा लेकिन अन्य प्रकार के पुनरावृत्त कार्यों के लिए जो आइटमों की संख्या में वास्तव में रैखिक हैं, सूची की समझें बहुत तेजी से हमेशा तेज होती हैं यह विशेष रूप से सच है अगर आपको सूची में कई परीक्षण करने की आवश्यकता है; आप पहले से ही निर्मित सूची की समझ से बहुत तेजी से पुनरावृति कर सकते हैं:

 >>> incache = [2999999 in sublist for sublist in lol] >>> get_list = lambda: incache >>> get_gen = lambda: (2999999 in sublist for sublist in lol) >>> %timeit for i in get_list(): pass 100 loops, best of 3: 18.6 ms per loop >>> %timeit for i in get_gen(): pass 1 loops, best of 3: 187 ms per loop 

इस मामले में, सूची की गहराई तीव्रता का एक क्रम है!

बेशक, यह केवल तब तक सच है जब तक आप स्मृति से बाहर नहीं चलाते जो मुझे मेरे अंतिम बिंदु पर लाता है जनरेटर का उपयोग करने के दो मुख्य कारण हैं: लघु सर्किटिंग का लाभ उठाने के लिए और मेमोरी को बचाने के लिए। बहुत बड़ी सैकड़ों / इटेरेट्री के लिए, जनरेटर जाने का स्पष्ट तरीका है, क्योंकि वे मेमोरी को बचाते हैं। लेकिन अगर शॉर्ट सर्किटिंग एक विकल्प नहीं है, तो आप स्पीड के लिए जेनरेटर का चयन नहीं करते हैं आप उन्हें स्मृति को बचाने के लिए चुना है, और यह हमेशा एक व्यापार बंद है

पूरी तरह से डेटा पर निर्भर करता है

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

स्मरण करो कि जैसे सीपीथन सूचियों का विस्तार किया गया है, सूची को 4, 8, 16, 25, 35, 46, 58, 72, 88 के विकास पैटर्न में बदल दिया गया है …। बड़ी सूची के लिए, पायथन आपके डेटा के आकार की तुलना में 4x अधिक मेमोरी तक आवंटित कर सकता है। एक बार जब आप वी.एम. मारते हैं — वास्तव में स्लोवॉव! लेकिन, जैसा कि कहा गया है, छोटे डेटा सेटों के लिए जेनरेटर की तुलना में सूची की समझें तेज़ हैं।

मामले 1 पर विचार करें, सूचियों की एक 2×26 सूची:

 LoL=[[c1,c2] for c1,c2 in zip(string.ascii_lowercase,string.ascii_uppercase)] def lc_d(item='d'): return item in [i for sub in LoL for i in sub] def ge_d(item='d'): return item in (y for x in LoL for y in x) def any_lc_d(item='d'): return any(item in x for x in LoL) def any_gc_d(item='d'): return any([item in x for x in LoL]) def lc_z(item='z'): return item in [i for sub in LoL for i in sub] def ge_z(item='z'): return item in (y for x in LoL for y in x) def any_lc_z(item='z'): return any(item in x for x in LoL) def any_gc_z(item='z'): return any([item in x for x in LoL]) cmpthese.cmpthese([lc_d,ge_d,any_gc_d,any_gc_z,any_lc_d,any_lc_z, lc_z, ge_z]) 

इन समय में परिणाम:

  rate/sec ge_z lc_z lc_d any_lc_z any_gc_z any_gc_d ge_d any_lc_d ge_z 124,652 -- -10.1% -16.6% -44.3% -46.5% -48.5% -76.9% -80.7% lc_z 138,678 11.3% -- -7.2% -38.0% -40.4% -42.7% -74.3% -78.6% lc_d 149,407 19.9% 7.7% -- -33.3% -35.8% -38.2% -72.3% -76.9% any_lc_z 223,845 79.6% 61.4% 49.8% -- -3.9% -7.5% -58.5% -65.4% any_gc_z 232,847 86.8% 67.9% 55.8% 4.0% -- -3.7% -56.9% -64.0% any_gc_d 241,890 94.1% 74.4% 61.9% 8.1% 3.9% -- -55.2% -62.6% ge_d 539,654 332.9% 289.1% 261.2% 141.1% 131.8% 123.1% -- -16.6% any_lc_d 647,089 419.1% 366.6% 333.1% 189.1% 177.9% 167.5% 19.9% -- 

अब मामले 2 पर विचार करें, जो एलसी और जीन के बीच व्यापक असमानता दिखाते हैं। इस मामले में, हम 100 x 97 x 97 सूची में एक प्रकार की संरचना की तलाश कर रहे हैं:

 LoL=[[str(a),str(b),str(c)] for a in range(100) for b in range(97) for c in range(97)] def lc_10(item='10'): return item in [i for sub in LoL for i in sub] def ge_10(item='10'): return item in (y for x in LoL for y in x) def any_lc_10(item='10'): return any([item in x for x in LoL]) def any_gc_10(item='10'): return any(item in x for x in LoL) def lc_99(item='99'): return item in [i for sub in LoL for i in sub] def ge_99(item='99'): return item in (y for x in LoL for y in x) def any_lc_99(item='99'): return any(item in x for x in LoL) def any_gc_99(item='99'): return any([item in x for x in LoL]) cmpthese.cmpthese([lc_10,ge_10,any_lc_10,any_gc_10,lc_99,ge_99,any_lc_99,any_gc_99],c=10,micro=True) 

इन समय में परिणाम:

  rate/sec usec/pass ge_99 lc_99 lc_10 any_lc_99 any_gc_99 any_lc_10 ge_10 any_gc_10 ge_99 3 354545.903 -- -20.6% -30.6% -60.8% -61.7% -63.5% -100.0% -100.0% lc_99 4 281678.295 25.9% -- -12.6% -50.6% -51.8% -54.1% -100.0% -100.0% lc_10 4 246073.484 44.1% 14.5% -- -43.5% -44.8% -47.4% -100.0% -100.0% any_lc_99 7 139067.292 154.9% 102.5% 76.9% -- -2.4% -7.0% -100.0% -100.0% any_gc_99 7 135748.100 161.2% 107.5% 81.3% 2.4% -- -4.7% -100.0% -100.0% any_lc_10 8 129331.803 174.1% 117.8% 90.3% 7.5% 5.0% -- -100.0% -100.0% ge_10 175,494 5.698 6221964.0% 4943182.0% 4318339.3% 2440446.0% 2382196.2% 2269594.1% -- -38.5% any_gc_10 285,327 3.505 10116044.9% 8036936.7% 7021036.1% 3967862.6% 3873157.1% 3690083.0% 62.6% -- 

जैसा कि आप देख सकते हैं – यह निर्भर करता है और यह एक ट्रेडऑफ है …

लोकप्रिय धारणा के विपरीत, मध्यम सीमाओं के लिए सूची के आकलन बहुत ठीक हैं इटरेटर प्रोटोकॉल का अर्थ है iterator.next () के लिए कॉल, और पायथन में फ़ंक्शन कॉल महंगे हैं।

बेशक, कुछ बिंदु पर जेनरेटर मेमोरी / सीपीयू ट्रेड-ऑफ का भुगतान करना शुरू हो जाएगा, लेकिन छोटे सेटों के लिए सूची की समझें बहुत ही कुशल हैं।