क्या "x <y <z" "x <y और y <z" से तेज है?


129

से यह पेज , हम जानते हैं:

andऑपरेटर की तुलना में जंजीर की तुलना तेजी से होती है । के x < y < zबजाय लिखें x < y and y < z

हालाँकि, मुझे निम्नलिखित कोड स्निपेट का परीक्षण करने का एक अलग परिणाम मिला:

$ python -m timeit "x = 1.2" "y = 1.3" "z = 1.8" "x < y < z"
1000000 loops, best of 3: 0.322 usec per loop
$ python -m timeit "x = 1.2" "y = 1.3" "z = 1.8" "x < y and y < z"
1000000 loops, best of 3: 0.22 usec per loop
$ python -m timeit "x = 1.2" "y = 1.3" "z = 1.1" "x < y < z"
1000000 loops, best of 3: 0.279 usec per loop
$ python -m timeit "x = 1.2" "y = 1.3" "z = 1.1" "x < y and y < z"
1000000 loops, best of 3: 0.215 usec per loop

ऐसा लगता है कि x < y and y < zकी तुलना में तेज है x < y < zक्यों?

इस साइट में कुछ पोस्ट खोजने के बाद (जैसे यह ) मुझे पता है कि "केवल एक बार मूल्यांकन किया गया" महत्वपूर्ण है x < y < z, हालांकि मैं अभी भी उलझन में हूं। आगे का अध्ययन करने के लिए, मैंने इन दो कार्यों का उपयोग करके अलग कर दिया dis.dis:

import dis

def chained_compare():
        x = 1.2
        y = 1.3
        z = 1.1
        x < y < z

def and_compare():
        x = 1.2
        y = 1.3
        z = 1.1
        x < y and y < z

dis.dis(chained_compare)
dis.dis(and_compare)

और आउटपुट है:

## chained_compare ##

  4           0 LOAD_CONST               1 (1.2)
              3 STORE_FAST               0 (x)

  5           6 LOAD_CONST               2 (1.3)
              9 STORE_FAST               1 (y)

  6          12 LOAD_CONST               3 (1.1)
             15 STORE_FAST               2 (z)

  7          18 LOAD_FAST                0 (x)
             21 LOAD_FAST                1 (y)
             24 DUP_TOP
             25 ROT_THREE
             26 COMPARE_OP               0 (<)
             29 JUMP_IF_FALSE_OR_POP    41
             32 LOAD_FAST                2 (z)
             35 COMPARE_OP               0 (<)
             38 JUMP_FORWARD             2 (to 43)
        >>   41 ROT_TWO
             42 POP_TOP
        >>   43 POP_TOP
             44 LOAD_CONST               0 (None)
             47 RETURN_VALUE

## and_compare ##

 10           0 LOAD_CONST               1 (1.2)
              3 STORE_FAST               0 (x)

 11           6 LOAD_CONST               2 (1.3)
              9 STORE_FAST               1 (y)

 12          12 LOAD_CONST               3 (1.1)
             15 STORE_FAST               2 (z)

 13          18 LOAD_FAST                0 (x)
             21 LOAD_FAST                1 (y)
             24 COMPARE_OP               0 (<)
             27 JUMP_IF_FALSE_OR_POP    39
             30 LOAD_FAST                1 (y)
             33 LOAD_FAST                2 (z)
             36 COMPARE_OP               0 (<)
        >>   39 POP_TOP
             40 LOAD_CONST               0 (None)

ऐसा लगता है कि की x < y and y < zतुलना में कम असंतुष्ट कमांड है x < y < z। क्या मुझे इससे x < y and y < zअधिक तेज विचार करना चाहिए x < y < z?

इंटेल (R) Xeon (R) CPU E5640 @ 2.67GHz पर Python 2.7.6 के साथ परीक्षण किया गया।


8
अधिक असंतुष्ट आदेशों का अर्थ अधिक जटिलता या धीमा कोड नहीं है। हालाँकि आपके timeitपरीक्षणों को देखकर मुझे इसमें दिलचस्पी हुई।
मार्को बोनेली

6
मैंने "एक बार मूल्यांकन किया" से आने वाले गति अंतर को मान लिया है जब yकेवल एक वैरिएबल लुकअप नहीं है, लेकिन एक फ़ंक्शन कॉल जैसी अधिक महंगी प्रक्रिया है? यानी 10 < max(range(100)) < 15की तुलना में तेज है10 < max(range(100)) and max(range(100)) < 15 कारण max(range(100))दोनों तुलनाओं के लिए एक बार कहा जाता है।
zehnpaard 8

2
@MarcoBonelli यह तब होता है जब असंतुष्ट कोड 1) में लूप नहीं होते हैं और 2) हर एक बायटेकोड बहुत तेज होता है, क्योंकि उस समय मेनलोप का ओवरहेड महत्वपूर्ण हो जाता है।
बकुरीउ

2
शाखा की भविष्यवाणी आपके परीक्षणों को गड़बड़ कर सकती है।
कोरी ओगबर्न

2
@zehnpaard मैं आपसे सहमत हूं। जब "y" एक साधारण मूल्य से अधिक होता है (उदाहरण के लिए, फ़ंक्शन कॉल या गणना) तो मैं इस तथ्य की अपेक्षा करूंगा कि "y" का मूल्यांकन एक बार x <y <z में अधिक ध्यान देने योग्य प्रभाव हो। प्रस्तुत मामले में हम त्रुटि सलाखों के भीतर हैं: (असफल) शाखा की भविष्यवाणी, कम-से-इष्टतम बाइटेकोड, और अन्य प्लेटफ़ॉर्म / सीपीयू-विशिष्ट प्रभाव के प्रभाव। यह सामान्य नियम को अमान्य नहीं करता है जो एक बार मूल्यांकन करना बेहतर है (साथ ही अधिक पठनीय है), लेकिन यह दर्शाता है कि यह चरम सीमा पर अधिक मूल्य नहीं जोड़ सकता है।
मार्टीमैकगाइवर

जवाबों:


111

अंतर यह है कि x < y < z yकेवल एक बार मूल्यांकन किया जाता है। यह बड़ा अंतर नहीं है यदि y एक चर है, लेकिन यह तब होता है जब यह एक फ़ंक्शन कॉल होता है, जिसे गणना करने में कुछ समय लगता है।

from time import sleep
def y():
    sleep(.2)
    return 1.3
%timeit 1.2 < y() < 1.8
10 loops, best of 3: 203 ms per loop
%timeit 1.2 < y() and y() < 1.8
1 loops, best of 3: 405 ms per loop

18
बेशक, एक शब्दार्थ अंतर भी हो सकता है। न केवल y () दो अलग-अलग मान लौटा सकता है, बल्कि एक चर के साथ x <y में कम-से-कम ऑपरेटर के मूल्यांकन से y का मान बदल सकता है। यही कारण है कि यह अक्सर बाइट कोड में अनुकूलित नहीं होता है (यदि y एक गैर-स्थानीय चर है या एक बंद में भाग ले रहा है, उदाहरण के लिए)
रैंडम 832

बस जिज्ञासु, आपको sleep()फ़ंक्शन के अंदर की आवश्यकता क्यों थी ?
प्रोफेसर

@Prof एक फ़ंक्शन को अनुकरण करना है जो परिणाम की गणना करने में कुछ समय लेता है। यदि कार्य तुरंत वापस आते हैं, तो दो समय परिणाम के बीच बहुत अंतर नहीं होगा।
रोब

@ रोब क्यों इसमें बहुत अंतर नहीं होगा? यह 3ms बनाम 205ms होगा, जो इसे अच्छी तरह से दर्शाता है कि यह पर्याप्त नहीं है?
प्रोफेसर

@Prof आप इस बिंदु को याद कर रहे हैं कि y () की गणना दो बार की गई है, इसलिए 1x200ms के बजाय 2x200ms है। बाकी (3/5 एमएस) समय माप में अप्रासंगिक शोर है।
रोब

22

आपके द्वारा परिभाषित दोनों कार्यों के लिए इष्टतम बाईटेकोड होगा

          0 LOAD_CONST               0 (None)
          3 RETURN_VALUE

क्योंकि तुलना के परिणाम का उपयोग नहीं किया जाता है। तुलना के परिणाम को वापस करके स्थिति को और अधिक रोचक बनाते हैं। संकलन समय पर पता नहीं चलने दें।

def interesting_compare(y):
    x = 1.1
    z = 1.3
    return x < y < z  # or: x < y and y < z

फिर से, तुलना के दो संस्करण शब्दार्थ समान हैं, इसलिए इष्टतम बायटेकोड दोनों निर्माणों के लिए समान है। सबसे अच्छा मैं इसे बाहर काम कर सकते हैं, यह इस तरह दिखेगा। मैंने प्रत्येक ओपकोड के पहले और बाद में स्टैक सामग्री के साथ प्रत्येक पंक्ति को एनोटेट किया है, फोर्थ नोटेशन में (दाईं ओर स्टैक का शीर्ष, --पहले और बाद में विभाजित होता है, अनुगामी ?कुछ ऐसा इंगित करता है जो हो सकता है या नहीं भी हो सकता है)। ध्यान दें कि जो RETURN_VALUEकुछ भी घटित होता है उसे वापस लौटाए गए मूल्य के नीचे स्टैक पर छोड़ दिया जाता है।

          0 LOAD_FAST                0 (y)    ;          -- y
          3 DUP_TOP                           ; y        -- y y
          4 LOAD_CONST               0 (1.1)  ; y y      -- y y 1.1
          7 COMPARE_OP               4 (>)    ; y y 1.1  -- y pred
         10 JUMP_IF_FALSE_OR_POP     19       ; y pred   -- y
         13 LOAD_CONST               1 (1.3)  ; y        -- y 1.3
         16 COMPARE_OP               0 (<)    ; y 1.3    -- pred
     >>  19 RETURN_VALUE                      ; y? pred  --

यदि भाषा का कार्यान्वयन, CPython, PyPy, जो भी हो, इस बाइटेकोड (या संचालन के अपने स्वयं के समतुल्य अनुक्रम) को दोनों भिन्नताओं के लिए उत्पन्न नहीं करता है, जो कि उस बायटेकोड कंपाइलर की खराब गुणवत्ता को प्रदर्शित करता है । आपके द्वारा उपरोक्त पोस्ट किए गए बाइटकोड दृश्यों से प्राप्त करना एक हल की गई समस्या है (मुझे लगता है कि इस मामले के लिए आपको सभी की आवश्यकता है निरंतर तह , मृत कोड उन्मूलन , और स्टैक की सामग्री का बेहतर मॉडलिंग; आम सबप्रेसेशन उन्मूलन भी सस्ता और मूल्यवान होगा ; ), और आधुनिक भाषा कार्यान्वयन में ऐसा नहीं करने के लिए वास्तव में कोई बहाना नहीं है।

अब, ऐसा होता है कि भाषा के सभी मौजूदा कार्यान्वयन में खराब-गुणवत्ता वाले बायटेकोड संकलक होते हैं। लेकिन आपको कोडिंग करते समय इसे अनदेखा करना चाहिए ! प्रीटेक बाइटकोड कंपाइलर अच्छा है, और सबसे पठनीय लिखें कोड । यह संभवत: वैसे भी काफी तेज होगा। यदि ऐसा नहीं है, तो पहले एल्गोरिथम सुधार देखें, और साइथन को एक दूसरा प्रयास दें - जो कि आपके द्वारा लागू किए जाने वाले किसी भी अभिव्यक्ति-स्तर की तुलना में समान प्रयास के लिए कहीं अधिक सुधार प्रदान करेगा।


वैसे सभी आशावादों में से सबसे महत्वपूर्ण पहले स्थान पर संभव होगा: इनलाइनिंग। और यह गतिशील भाषाओं के लिए एक "हल की गई समस्या" से बहुत दूर है जो फ़ंक्शन के कार्यान्वयन को गतिशील रूप से बदलने की अनुमति देता है (संभव है - हालांकि - हॉटस्पॉट समान चीजें कर सकता है और ग्रेगल जैसी चीजें पायथन और अन्य गतिशील भाषाओं के लिए इस प्रकार के अनुकूलन को उपलब्ध कराने पर काम कर रही हैं। )। और चूंकि फ़ंक्शन स्वयं को विभिन्न मॉड्यूल से कॉल किया जा सकता है (या एक कॉल गतिशील रूप से उत्पन्न हो सकता है!) आप वास्तव में वहां ये अनुकूलन नहीं कर सकते।
वू डे

1
@Voo मेरा हाथ से अनुकूलित बाइटकोड में मनमाने ढंग से गतिशीलता की उपस्थिति में भी मूल के समान ही शब्दार्थ है (एक अपवाद: एक <b> b> a माना जाता है)। इसके अलावा, इनलाइनिंग ओवररेटेड है। यदि आप इसका बहुत अधिक उपयोग करते हैं - और यह बहुत आसान है कि आप इसे बहुत अधिक करें - आप आई-कैश को उड़ा दें और आपके द्वारा प्राप्त की गई सभी चीजें खो दें और फिर कुछ।
zwol

आप सही हैं, मैंने सोचा था कि आप चाहते थे कि आप interesting_compareशीर्ष पर अपने साधारण बाइटकोड को अनुकूलित करें (जो केवल इनलाइनिंग के साथ काम करेगा)। पूरी तरह से अपमानजनक लेकिन: Inlining किसी भी संकलक के लिए सबसे आवश्यक अनुकूलन में से एक है। आप हॉटस्पॉट को वास्तविक कार्यक्रमों पर कहने के लिए कुछ बेंचमार्क चलाने का प्रयास कर सकते हैं (कुछ गणित परीक्षण नहीं जो अपना 99% एक हॉट लूप में खर्च करते हैं जो कि अनुकूलित है [और इसलिए आपके पास किसी भी तरह से अधिक फ़ंक्शन कॉल नहीं है]) जब आप इसे अक्षम करते हैं किसी भी चीज़ को इनलाइन करने की क्षमता - आपको बड़े रिग्रेशन दिखाई देंगे।
वीयू

@Voo हाँ, शीर्ष पर सरल बायोटेक को ओपी के मूल कोड का "इष्टतम संस्करण" माना जाता था, नहीं interesting_compare
zwol

"एक अपवाद: एक <b> b> एक माना जाता है" → जो कि केवल पायथन में सच नहीं है। इसके अलावा, मुझे लगता है कि सीपीथॉन सही मायने में संचालन को yनहीं मान सकता है, स्टैक को न बदलें, क्योंकि इसमें बहुत सारे डिबग टूल हैं।
विड्रैक

8

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

हालांकि महत्वपूर्ण अंतर यह है कि x<y and y<zदूसरे में yदो बार मूल्यांकन किया जाना चाहिए यदि x<yसत्य का मूल्यांकन किया जाता है, तो इसका यह अर्थ है कि मूल्यांकन में yकाफी समय लगता है या दुष्प्रभाव होता है।

अधिकांश परिदृश्यों में आपको x<y<zइस तथ्य के बावजूद उपयोग करना चाहिए कि यह कुछ धीमा है।


6

सबसे पहले, आपकी तुलना बहुत अधिक अर्थहीन है क्योंकि एक प्रदर्शन में सुधार प्रदान करने के लिए दो अलग-अलग निर्माण पेश नहीं किए गए थे , इसलिए आपको यह तय नहीं करना चाहिए कि उस पर आधारित दूसरे के स्थान पर एक का उपयोग करना है या नहीं।

x < y < zनिर्माण:

  1. स्पष्ट और इसके अर्थ में अधिक प्रत्यक्ष है।
  2. इसका शब्दार्थ यह है कि आप तुलना के "गणितीय अर्थ" से क्या उम्मीद करेंगे: बेदखली x, yऔर z एक बार और जांचें कि क्या पूरी स्थिति रखती है। कई बार andमूल्यांकन करके शब्दार्थों का उपयोग करना y, जो परिणाम को बदल सकता है ।

इसलिए यदि आप चाहते हैं और यदि , शब्दार्थ के आधार पर दूसरे के स्थान पर एक का चयन करें वे समकक्ष हैं, तो क्या एक दूसरे की तुलना में अधिक पठनीय है।

इसने कहा: अधिक असंतुष्ट कोड धीमा कोड नहीं करता है । हालाँकि अधिक बाइटकोड संचालन निष्पादित करने का मतलब है कि प्रत्येक ऑपरेशन सरल है और फिर भी इसे मुख्य लूप के पुनरावृत्ति की आवश्यकता है। इसका मतलब यह है कि यदि आप जो ऑपरेशन कर रहे हैं, वह बहुत तेज़ है (जैसे कि स्थानीय वैरिएबल लुकअप जैसा आप कर रहे हैं), तो अधिक बाइटकोड संचालन निष्पादित करने का ओवरहेड मायने रख सकता है।

लेकिन ध्यान दें कि यह परिणाम अधिक सामान्य स्थिति में नहीं है, केवल उस "सबसे खराब स्थिति" में है जिसे आप प्रोफाइल करते हैं। जैसा कि दूसरों ने नोट किया है, यदि आप बदलते हैंy किसी ऐसी चीज़ में करते हैं जिसमें थोड़ा और समय लगता है तो आप देखेंगे कि परिणाम बदल जाते हैं, क्योंकि जंजीर अंकन केवल एक बार इसका मूल्यांकन करता है।

सारांश:

  • प्रदर्शन से पहले शब्दार्थ पर विचार करें।
  • पठनीयता का ध्यान रखें।
  • माइक्रो बेंचमार्क पर भरोसा न करें। हमेशा विभिन्न प्रकार के मापदंडों के साथ प्रोफ़ाइल देखें कि कैसे एक फ़ंक्शन / अभिव्यक्ति समय उक्त मापदंडों के संबंध में व्यवहार करता है और विचार करता है कि आप इसे कैसे उपयोग करने की योजना बनाते हैं।

5
मुझे लगता है कि आपके उत्तर में सीधा और प्रासंगिक तथ्य शामिल नहीं है कि प्रश्न के विशेष मामले में उद्धृत पृष्ठ - तैरने की तुलना, बस गलत है। जंजीर तुलना तेज नहीं है जैसा कि दोनों मापों में और उत्पन्न बायोटेक में देखा जाता है।
pvg

30
"शायद आप प्रदर्शन के बारे में नहीं सोच रहे होंगे" के साथ टैग किए गए प्रश्न का उत्तर देना मेरे लिए उपयोगी नहीं लगता। आप सामान्य प्रोग्रामिंग सिद्धांतों के प्रश्नकर्ता की समझ के बारे में संभावित रूप से संरक्षण की धारणा बना रहे हैं और फिर हाथ पर मुद्दे के बजाय उनके बारे में बात कर रहे हैं।
बेन मिलवुड

@Veerdac आप टिप्पणी को गलत बता रहे हैं। ओपी पर निर्भर होने वाले मूल दस्तावेज में प्रस्तावित अनुकूलन न्यूनतम पर तैरने के मामले में गलत है। यह तेज नहीं है।
pvg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.