अजगर: * और ** / और sqrt () से अधिक तेज़ क्यों हैं?


80

अपने कोड का अनुकूलन करते समय मुझे निम्नलिखित का एहसास हुआ:

>>> from timeit import Timer as T
>>> T(lambda : 1234567890 / 4.0).repeat()
[0.22256922721862793, 0.20560789108276367, 0.20530295372009277]
>>> from __future__ import division
>>> T(lambda : 1234567890 / 4).repeat()
[0.14969301223754883, 0.14155197143554688, 0.14141488075256348]
>>> T(lambda : 1234567890 * 0.25).repeat()
[0.13619112968444824, 0.1281130313873291, 0.12830305099487305]

और भी:

>>> from math import sqrt
>>> T(lambda : sqrt(1234567890)).repeat()
[0.2597470283508301, 0.2498021125793457, 0.24994492530822754]
>>> T(lambda : 1234567890 ** 0.5).repeat()
[0.15409398078918457, 0.14059877395629883, 0.14049601554870605]

मुझे लगता है कि यह सी में अजगर को लागू करने के तरीके के साथ करना है, लेकिन मुझे आश्चर्य है कि अगर कोई यह समझाने की परवाह करेगा कि ऐसा क्यों है?


आपके प्रश्न के लिए आपके द्वारा स्वीकार किया गया उत्तर (जो मैं मानता हूं कि आपके वास्तविक प्रश्न का उत्तर है) का आपके प्रश्न शीर्षक से बहुत अधिक लेना-देना नहीं है। क्या आप इसे निरंतर तह के साथ कुछ करने के लिए संपादित कर सकते हैं?
ज़ैन लिंक्स

1
@ZanLynx - हाय। क्या आप स्पष्ट करना चाहेंगे? मुझे लगता है कि प्रश्न शीर्षक ठीक वही व्यक्त करता है जो मैं जानना चाहता था (क्यों एक्स वाई की तुलना में तेज है) और जो जवाब मैंने चुना है वह ठीक यही करता है ... मुझे एक परिपूर्ण मैच लगता है ... लेकिन शायद मैं कुछ अनदेखी कर रहा हूं?
मैक

8
गुणन और शक्ति फ़ंक्शन हमेशा विभाजन और sqrt () फ़ंक्शन की वजह से बहुत तेज होते हैं क्योंकि वे बहुत प्रकृति के होते हैं। डिवीजन और रूट ऑपरेशंस को आम तौर पर महीन और बारीक अंदाजों की एक श्रृंखला का उपयोग करना पड़ता है और सीधे गुणन जैसे सही उत्तर पर नहीं जा सकते।
ज़ैन लिंक्स

मुझे ऐसा लगता है कि प्रश्न शीर्षक को इस तथ्य के बारे में कुछ कहना चाहिए कि मूल्य सभी शाब्दिक स्थिरांक हैं, जो उत्तर के लिए महत्वपूर्ण है। विशिष्ट हार्डवेयर पर, पूर्णांक और एफपी गुणा और जोड़ / घटाना सस्ते होते हैं; पूर्णांक और FP div, और FP sqrt, सभी महंगे हैं (शायद 3x बदतर विलंबता, और FP mul की तुलना में 10 गुना बदतर थ्रूपुट)। (अधिकांश सीपीयू इन क्रियाओं को क्यूब-रूट या पॉव () या जो भी हो, के विपरीत सिंगल एसम निर्देश के रूप में हार्डवेयर में कार्यान्वित करते हैं।
पीटर कॉर्ड्स

1
लेकिन मुझे आश्चर्य नहीं होगा अगर पायथन दुभाषिया उपरि अभी भी mul और div asm निर्देशों के बीच के अंतर को कम कर देता है। मजेदार तथ्य: x86 पर, एफपी डिवीजन आमतौर पर पूर्णांक विभाजन की तुलना में अधिक प्रदर्शन होता है। ( agner.org/optimize )। इंटेल स्काइलेक पर विभाजित 64-बिट पूर्णांक में 42-95 चक्रों की एक विलंबता है, 32-बिट पूर्णांक के लिए 26 चक्र बनाम, बनाम डबल-सटीक एफपी के लिए 14 चक्र। (64-बिट पूर्णांक गुणा 3 चक्र विलंबता है, FP mul 4 है)। थ्रूपुट के अंतर और भी बड़े हैं (int / FP mul और add, प्रत्येक कम से कम एक घड़ी है, लेकिन विभाजन और sqrt पूरी तरह से पाइपलाइन नहीं किए गए हैं।)
पीटर कॉर्ड्स

जवाबों:


114

आपके परिणामों के लिए (कुछ अप्रत्याशित) कारण यह है कि पायथन फ्लोटिंग-पॉइंट गुणा और घातांक को शामिल करने वाले निरंतर अभिव्यक्तियों को मोड़ना लगता है, लेकिन विभाजन नहीं। math.sqrt()पूरी तरह से एक अलग जानवर है क्योंकि इसके लिए कोई बायटेकोड नहीं है और इसमें एक फ़ंक्शन कॉल शामिल है।

पायथन 2.6.5 पर, निम्न कोड:

x1 = 1234567890.0 / 4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)

निम्नलिखित बायोटेक के संकलन:

  # x1 = 1234567890.0 / 4.0
  4           0 LOAD_CONST               1 (1234567890.0)
              3 LOAD_CONST               2 (4.0)
              6 BINARY_DIVIDE       
              7 STORE_FAST               0 (x1)

  # x2 = 1234567890.0 * 0.25
  5          10 LOAD_CONST               5 (308641972.5)
             13 STORE_FAST               1 (x2)

  # x3 = 1234567890.0 ** 0.5
  6          16 LOAD_CONST               6 (35136.418286444619)
             19 STORE_FAST               2 (x3)

  # x4 = math.sqrt(1234567890.0)
  7          22 LOAD_GLOBAL              0 (math)
             25 LOAD_ATTR                1 (sqrt)
             28 LOAD_CONST               1 (1234567890.0)
             31 CALL_FUNCTION            1
             34 STORE_FAST               3 (x4)

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

यदि आप निरंतर तह के प्रभाव को समाप्त करते हैं, तो अलग गुणन और विभाजन के लिए बहुत कम है:

In [16]: x = 1234567890.0

In [17]: %timeit x / 4.0
10000000 loops, best of 3: 87.8 ns per loop

In [18]: %timeit x * 0.25
10000000 loops, best of 3: 91.6 ns per loop

math.sqrt(x)वास्तव में थोड़ी तेजी से x ** 0.5, संभवतः इसलिए है क्योंकि यह बाद का एक विशेष मामला है और इसलिए ओवरहेड्स के बावजूद इसे अधिक कुशलता से किया जा सकता है:

In [19]: %timeit x ** 0.5
1000000 loops, best of 3: 211 ns per loop

In [20]: %timeit math.sqrt(x)
10000000 loops, best of 3: 181 ns per loop

2011-11-16 को संपादित करें: निरंतर अभिव्यक्ति तह पायथन के पीपहोल अनुकूलक द्वारा किया जाता है। स्रोत कोड ( peephole.c) में निम्न टिप्पणी है जो बताती है कि निरंतर विभाजन क्यों नहीं मुड़ा है:

    case BINARY_DIVIDE:
        /* Cannot fold this operation statically since
           the result can depend on the run-time presence
           of the -Qnew flag */
        return 0;

-Qnewझंडा "सच विभाजन" में परिभाषित सक्षम बनाता है पीईपी 238


2
शायद यह विभाजन-दर-शून्य के खिलाफ "सुरक्षा" है?
ह्यूगोमग

2
@ मिस्सिंगो: यह मेरे लिए अस्पष्ट है कि किसी भी ऐसे "संरक्षण" की आवश्यकता क्यों होगी क्योंकि दोनों तर्कों को संकलन समय पर जाना जाता है, और इसलिए इसका परिणाम है (जो कि: + inf, -inf, NaN) में से एक है।
एनपीई

13
लगातार तह /पायथन 3 के साथ काम करता है , और //पायथन 2 और 3 में। इसलिए सबसे अधिक संभावना है कि यह इस तथ्य का एक परिणाम है कि /अजगर 2 में अलग-अलग अर्थ हो सकते हैं। शायद जब लगातार तह किया जाता है, तो यह अभी तक ज्ञात नहीं है कि क्या from __future__ import divisionहै प्रभाव में?
अंतरजाल

4
@aix - 1./0.पायथन 2.7 में परिणाम नहीं है NaNलेकिन ए ZeroDivisionError
detly

2
@Caridorc: पायथन को बायटेकोड (.pyc files) में संकलित किया गया है, जिसे फिर पायथन रन टाइम द्वारा व्याख्यायित किया जाता है। बाइटकोड असेंबली / मशीन कोड (जो एक सी संकलक उदाहरण के लिए उत्पन्न करता है) के समान नहीं है। डिस मॉड्यूल का उपयोग बाइटकोड की जांच करने के लिए किया जा सकता है जो किसी दिए गए कोड टुकड़े को संकलित करता है।
टोनी सफ़ोकल 66
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.