2010-04-24 13 views
10

मेरे पास एक फ़ंक्शन foo (i) है जो एक पूर्णांक लेता है और निष्पादित करने में काफी समय लगता है। वहाँ आरंभ की निम्न तरीकों में से किसी के बीच एक महत्वपूर्ण प्रदर्शन अंतर एक हो जाएगा: (। मुझे परवाह नहीं है कि क्या उत्पादन एक सूची या एक numpy सरणी है)सूची समझ, मानचित्र, और numpy.vectorize प्रदर्शन

a = [foo(i) for i in xrange(100)] 

a = map(foo, range(100)) 

vfoo = numpy.vectorize(foo) 
a = vfoo(range(100)) 

वहाँ है एक बेहतर तरीका है?

+13

क्यों नहीं यह समय की कोशिश? – mpen

उत्तर

9

मेरी पहली टिप्पणी यह ​​है कि आपको अपने सभी उदाहरणों में xrange() या range() का उपयोग करना चाहिए। यदि आप उन्हें मिलाते हैं, तो आप सेब और संतरे की तुलना कर रहे हैं।

मैं दूसरा @ गेबे की धारणा है कि यदि आपके पास बहुत सारी डेटा संरचनाएं हैं और वे बड़े हैं, तो numpy समग्र जीतना चाहिए ... बस ध्यान रखें कि अधिकांश समय सी पाइथन से तेज है, लेकिन फिर, सबसे अधिक उस समय, पीपीपी सीपीथॉन से तेज है। :-)

जहाँ तक listcomps बनाम map() कॉल जाना ... एक 101 फ़ंक्शन कॉल, जबकि अन्य एक बनाता है 102. आप समय में एक महत्वपूर्ण अंतर दिखाई नहीं देंगे, के रूप में timeit मॉड्यूल के रूप में उपयोग करते हुए नीचे दिखाया गया है बनाता है @Mark का सुझाव दिया:

  • सूची समझ

    $ python -m timeit "def foo(x):pass; [foo(i) for i in range(100)]"
    1000000 loops, best of 3: 0.216 usec per loop
    $ python -m timeit "def foo(x):pass; [foo(i) for i in range(100)]"
    1000000 loops, best of 3: 0.21 usec per loop
    $ python -m timeit "def foo(x):pass; [foo(i) for i in range(100)]"
    1000000 loops, best of 3: 0.212 usec per loop

  • map() समारोह कॉल

    $ python -m timeit "def foo(x):pass; map(foo, range(100))"
    1000000 loops, best of 3: 0.216 usec per loop
    $ python -m timeit "def foo(x):pass; map(foo, range(100))"

    $ python -m timeit "def foo(x):pass; map(foo, range(100))"
    1000000 loops, best of 3: 0.215 usec per loop

सभी कि हालांकि कहा साथ

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

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

a = (foo(i) for i in range(100)) 
अधिक यात्रा की तर्ज पर

, 2.x विज्ञप्ति के शेष के लिए xrange() करने के लिए सभी range() कॉल बदल तो उन्हें वापस स्विच: सबसे अच्छी बात यह है कि इसके वाक्य रचना listcomps की है कि लगभग समान है range() जब पाइथन 3 को xrange() के रूप में पोर्ट करते समय बदल दिया जाता है और इसका नाम बदलकर range() कर दिया जाता है। :-)

+0

ध्यान दें कि मानचित्र को एक फ़ंक्शन को परिभाषित करने की आवश्यकता होती है लेकिन सूची की समझ नहीं होती है, जो लाभ हो सकता है। यथार्थवादी सूची समझ का उपयोग वही होता है जैसे आपका कोड इनलाइन हो सकता है 'python -m timeit" def foo (x): पास; [रेंज में मेरे लिए कोई नहीं (100)] "' मेरी मशीन पर परिणाम देता है जो आपकी सूची समझ के समय के बारे में 2/3 लेता है। क्या यह वास्तव में ओपी चाहता है? मुझे नहीं पता कि यह है, लेकिन यह करता है दिखाएं कि ये प्रश्न नामुमकिन हैं और निष्कर्ष इस बात के बारे में और अधिक प्रतिबिंबित कर सकते हैं कि हम वास्तविक उपयोग के बारे में किसी भी चीज़ से हमारे उदाहरण का विरोध कैसे करते हैं। –

+0

काफी नहीं। 'मानचित्र()' को किसी फ़ंक्शन की आवश्यकता नहीं है, जैसे कि मानचित्र में (कोई नहीं , रेंज (100))। मेरे पास प्रदर्शन और सूची के बारे में लंबा समय है। 'नक्शा() 'लेकिन ओपी ने उस सवाल से नहीं पूछा, इसलिए मैं इसका उत्तर यहां नहीं दे सकता। मैं क्या कह सकता हूं कि * वास्तव में * सूची को गति दें, आपको उस फ़ंक्शन को अभिव्यक्ति में कम करना होगा और * * * (फ़ंक्शन के बजाए) का उपयोग करना होगा। फ़ंक्शन कॉल करने के लिए एक प्रदर्शन जुर्माना होता है जो एक तंग लूप में बड़ा होता है। – wescpy

+0

* ध्यान रखें सी पाइथन से हमेशा तेज है * ध्यान दें कि यह वास्तव में सच नहीं है। –

7

यदि फ़ंक्शन को निष्पादित करने में काफी समय लगता है, तो यह अप्रासंगिक है कि आप अपने आउटपुट को सरणी में कैसे मैप करते हैं। एक बार जब आप लाखों संख्याओं के सरणी में प्रवेश करना शुरू कर देते हैं, तो, numpy आपको स्मृति की एक महत्वपूर्ण मात्रा बचा सकता है।

+0

सहमति ... डाटा संरचनाओं की बड़ी संख्या को जब सी में संसाधित बनाम शुद्ध पायथन – wescpy

+2

ध्यान दें कि का उपयोग करते हुए' numpy.vectorize' नहीं है वास्तव में प्रभावी रूप से सबसे तेजी से कर रहे हैं वास्तविक numpy परिचालनों का उपयोग कर चीजों को सी में ले जाएं। –

3

सूची समझ सबसे तेज़ है, फिर मानचित्र, फिर मेरी मशीन पर numpy। Numpy कोड वास्तव में अन्य दो की तुलना में थोड़ा धीमा है, लेकिन यदि आप नीचे सूचीबद्ध समय में किया गया है तो श्रेणी (या xrange) के बजाय numpy.arange का उपयोग करते हैं तो अंतर बहुत कम है। इसके अलावा, यदि आप साइको का उपयोग करते हैं, तो सूची की समझ बढ़ जाती है जबकि अन्य दो मेरे लिए धीमे हो जाते हैं। मैंने आपके कोड की तुलना में संख्याओं के बड़े सरणी भी उपयोग किए हैं और मेरे foo फ़ंक्शन ने वर्ग रूट की गणना की है। यहां कुछ सामान्य समय दिए गए हैं।

psyco के बिना:

list comprehension: 47.5581952455 ms 
map: 51.9082732582 ms 
numpy.vectorize: 57.9601876775 ms 

psyco के साथ:

list comprehension: 30.4318844993 ms 
map: 96.4504427239 ms 
numpy.vectorize: 99.5858691538 ms 

मैं अजगर 2.6.4 और timeit मॉड्यूल का इस्तेमाल किया।

इन परिणामों के आधार पर, मैं कहूंगा कि शायद यह वास्तव में कोई फर्क नहीं पड़ता है जिसे आप प्रारंभ करने के लिए चुनते हैं। मैं शायद गति के आधार पर numpy one या सूची समझ का चयन करूंगा, लेकिन आखिरकार आपको सरणी के साथ जो कुछ भी कर रहे हैं उसे अपनी पसंद का मार्गदर्शन करना चाहिए।

+0

हम्म, क्षमा करें यह देर हो चुकी है। मैं कुछ समय के लिए पोस्ट करने और त्रुटियों को पाने की कोशिश कर रहा हूं। –

18
  • आप इसे क्यों अनुकूलित कर रहे हैं? क्या आपने काम किया है, परीक्षण कोड लिखा है, फिर अपने एल्गोरिदम profiled को अपने कोड की जांच की और पाया कि इसका अनुकूलन प्रभाव पड़ेगा? क्या आप इसे एक गहरे आंतरिक लूप में कर रहे हैं जहां आपने पाया कि आप अपना समय व्यतीत कर रहे हैं? यदि नहीं, परेशान मत करो।

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

  • आप कहते हैं कि इससे कोई फर्क नहीं पड़ता कि आप एक numpy सरणी या एक list साथ खत्म हो, लेकिन आप सूक्ष्म अनुकूलन इस तरह का कार्य कर रहे हैं अगर यह फर्क पड़ता है, उन के बाद से जब आप अलग तरह से प्रदर्शन करेंगे बाद में उनका इस्तेमाल करें। उस पर अपनी उंगली डालना मुश्किल हो सकता है, इसलिए उम्मीद है कि यह पूरी समस्या को समय से पहले मूक कर देगा।

  • स्पष्टता, पठनीयता, और आगे के लिए नौकरी के लिए सही उपकरण का उपयोग करना आम तौर पर बेहतर होता है। यह दुर्लभ है कि मुझे इन चीजों के बीच निर्णय लेने में कठिनाई होगी।

    • यदि मुझे numpy arrays की आवश्यकता है, तो मैं उनका उपयोग करूंगा। मैं इन्हें बड़े सजातीय सरणी या बहुआयामी डेटा संग्रहित करने के लिए उपयोग करता हूं। मैं उनका बहुत उपयोग करता हूं, लेकिन शायद ही कभी मुझे लगता है कि मैं एक सूची का उपयोग करना चाहता हूं।
      • अगर मैं इन उपयोग कर रहा था, मैं अपने कार्यों लिखने के लिए मेरा सबसे अच्छा होता पहले से ही vectorized तो मैं numpy.vectorize उपयोग करने के लिए नहीं था। उदाहरण के लिए, नीचे times_five कोई सजावट के साथ एक numpy सरणी पर इस्तेमाल किया जा सकता है।
    • अगर मैं numpy का उपयोग करने के कारण नहीं था, कि अगर मैं संख्यात्मक गणित की समस्याओं को सुलझाने या विशेष numpy सुविधाओं बहुआयामी सरणियों या जो कुछ भी उपयोग कर या भंडारण नहीं किया गया था कहने के लिए ...
      • तो है मेरे पास पहले से मौजूद फ़ंक्शन था, मैं map का उपयोग करूंगा। यही वह है।
      • यदि मेरे पास एक ऑपरेशन था जो एक छोटी अभिव्यक्ति के अंदर फिट था और मुझे फ़ंक्शन की आवश्यकता नहीं थी, तो मैं एक सूची समझ का उपयोग करता था।
      • अगर मैं सिर्फ सभी मामलों के लिए ऑपरेशन करना चाहता था लेकिन वास्तव में परिणाम को स्टोर करने की आवश्यकता नहीं थी, तो मैं लूप के लिए एक सादा उपयोग करता था।
      • कई मामलों में, मैं वास्तव में map का उपयोग करता हूं और समझता हूं 'आलसी समकक्षों की सूची: itertools.imap और जनरेटर अभिव्यक्तियां। ये कुछ मामलों में n के कारक द्वारा स्मृति उपयोग को कम कर सकते हैं और कभी-कभी अनावश्यक संचालन करने से बच सकते हैं।

यदि यह स्पष्ट हो बारी है इस जहां प्रदर्शन की समस्याओं, झूठ हो रही बात इस तरह की सही मुश्किल है। यह बहुत आम है कि लोगों को उनकी वास्तविक समस्याओं के लिए गलत खिलौना का मामला है। इससे भी बदतर, यह बेहद आम लोगों के आधार पर गूंगा सामान्य नियम बनाते हैं।

निम्नलिखित मामलों (timeme.py नीचे पोस्ट किया जाता है)

python -m timeit "from timeme import x, times_five; from numpy import vectorize" "vectorize(times_five)(x)" 
1000 loops, best of 3: 924 usec per loop 

python -m timeit "from timeme import x, times_five" "[times_five(item) for item in x]" 
1000 loops, best of 3: 510 usec per loop 

python -m timeit "from timeme import x, times_five" "map(times_five, x)" 
1000 loops, best of 3: 484 usec per loop 

एक भोली obsever निष्कर्ष निकालना होगा कि नक्शा इन विकल्पों में से सबसे अच्छा प्रदर्शन है पर विचार करें, लेकिन इस सवाल का जवाब अभी भी है "यह निर्भर करता है"। आपके द्वारा उपयोग किए जा रहे टूल के लाभों का उपयोग करने की शक्ति पर विचार करें: सूची की समझ आपको सरल कार्यों को परिभाषित करने से बचने देती है; यदि आप सही चीजें कर रहे हैं तो numpy आपको सी में चीजों को सदिश करने देता है।

python -m timeit "from timeme import x, times_five" "[item + item + item + item + item for item in x]" 
1000 loops, best of 3: 285 usec per loop 

python -m timeit "import numpy; x = numpy.arange(1000)" "x + x + x + x + x" 
10000 loops, best of 3: 39.5 usec per loop 

लेकिन यह सब कुछ नहीं है। एक एल्गोरिदम परिवर्तन की शक्ति पर विचार करें। यह और भी नाटकीय हो सकता है।

python -m timeit "from timeme import x, times_five" "[5 * item for item in x]" 
10000 loops, best of 3: 147 usec per loop 

python -m timeit "import numpy; x = numpy.arange(1000)" "5 * x" 
100000 loops, best of 3: 16.6 usec per loop 

कभी-कभी एक एल्गोरिदम परिवर्तन और भी प्रभावी हो सकता है। यह अधिक से अधिक प्रभावी होगा क्योंकि संख्याएं बड़ी हो जाती हैं।

python -m timeit "from timeme import square, x" "map(square, x)" 
10 loops, best of 3: 41.8 msec per loop 

python -m timeit "from timeme import good_square, x" "map(good_square, x)" 
1000 loops, best of 3: 370 usec per loop 

और अब भी, यह सब आपकी वास्तविक समस्या पर थोड़ा असर डाल सकता है। ऐसा लगता है कि अगर आप इसे सही तरीके से उपयोग कर सकते हैं तो numpy बहुत अच्छा है, लेकिन इसकी सीमाएं हैं: इन numpy उदाहरणों में से कोई भी सरणी में वास्तविक पायथन वस्तुओं का उपयोग नहीं किया। यह जटिल है कि क्या किया जाना चाहिए; बहुत कुछ भी और क्या होगा यदि हम सी डेटाटाइप का उपयोग करते हैं? ये पाइथन वस्तुओं की तुलना में कम मजबूत हैं। वे शून्य नहीं हैं। पूर्णांक अतिप्रवाह। आपको उन्हें पुनः प्राप्त करने के लिए कुछ अतिरिक्त काम करना है। वे स्थिर रूप से टाइप कर रहे हैं। कभी-कभी ये चीजें समस्याएं साबित होती हैं, यहां तक ​​कि अप्रत्याशित भी।

तो वहां आप जाते हैं: एक निश्चित उत्तर। "निर्भर करता है।"


# timeme.py 

x = xrange(1000) 

def times_five(a): 
    return a + a + a + a + a 

def square(a): 
    if a == 0: 
     return 0 

    value = a 
    for i in xrange(a - 1): 
     value += a 
    return value 

def good_square(a): 
    return a ** 2 
संबंधित मुद्दे