ऐसा इसलिए लगता है क्योंकि छोटी संख्याओं का गुणा CPython 3.5 में अनुकूलित होता है, इस तरह से कि छोटी संख्याओं द्वारा छोड़ी गई पाली नहीं होती है। सकारात्मक बाएं बदलाव हमेशा गणना के भाग के रूप में परिणाम को संग्रहीत करने के लिए एक बड़ा पूर्णांक ऑब्जेक्ट बनाते हैं, जबकि आपके परीक्षण में आपके द्वारा उपयोग किए गए सॉर्ट के गुणन के लिए, एक विशेष अनुकूलन इससे बचता है और सही आकार का पूर्णांक ऑब्जेक्ट बनाता है। इसे पायथन के पूर्णांक कार्यान्वयन के स्रोत कोड में देखा जा सकता है ।
क्योंकि पायथन में पूर्णांक मनमाने ढंग से सटीक होते हैं, उन्हें पूर्णांक अंक के बिट्स की संख्या की सीमा के साथ पूर्णांक "अंक" के सरणियों के रूप में संग्रहीत किया जाता है। तो सामान्य मामले में, पूर्णांकों से जुड़े संचालन एकल संचालन नहीं हैं, बल्कि इसके बजाय कई "अंकों" के मामले को संभालने की आवश्यकता है। में pyport.h , इस बिट सीमा के रूप में परिभाषित किया गया है अन्यथा 64-बिट प्लेटफॉर्म पर 30 बिट, या 15 बिट्स। (मैं स्पष्टीकरण को सरल रखने के लिए यहां से सिर्फ 30 पर कॉल करूंगा। लेकिन ध्यान दें कि यदि आप 32-बिट के लिए संकलित पायथन का उपयोग कर रहे थे, तो आपके बेंचमार्क का परिणाम इस बात पर निर्भर करेगा कि क्या x
32,768 से कम है या नहीं।)
जब किसी ऑपरेशन के इनपुट और आउटपुट इस 30-बिट सीमा के भीतर रहते हैं, तो ऑपरेशन को सामान्य तरीके के बजाय अनुकूलित तरीके से नियंत्रित किया जा सकता है। पूर्णांक गुणन कार्यान्वयन की शुरुआत निम्नानुसार है:
static PyObject *
long_mul(PyLongObject *a, PyLongObject *b)
{
PyLongObject *z;
CHECK_BINOP(a, b);
/* fast path for single-digit multiplication */
if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {
stwodigits v = (stwodigits)(MEDIUM_VALUE(a)) * MEDIUM_VALUE(b);
#ifdef HAVE_LONG_LONG
return PyLong_FromLongLong((PY_LONG_LONG)v);
#else
/* if we don't have long long then we're almost certainly
using 15-bit digits, so v will fit in a long. In the
unlikely event that we're using 30-bit digits on a platform
without long long, a large v will just cause us to fall
through to the general multiplication code below. */
if (v >= LONG_MIN && v <= LONG_MAX)
return PyLong_FromLong((long)v);
#endif
}
इसलिए जब दो पूर्णांकों को गुणा करते हैं जहां प्रत्येक 30-बिट अंक में फिट बैठता है, तो यह पूर्णांक के रूप में पूर्णांक के साथ काम करने के बजाय, CPython दुभाषिया द्वारा प्रत्यक्ष गुणन के रूप में किया जाता है। ( MEDIUM_VALUE()
पॉजिटिव पूर्णांक ऑब्जेक्ट पर कॉल किया जाता है, बस इसका पहला 30-बिट अंक प्राप्त होता है।) यदि परिणाम एक एकल 30-बिट अंक में फिट बैठता है, PyLong_FromLongLong()
तो यह अपेक्षाकृत कम संख्या में संचालन पर ध्यान देगा, और स्टोर करने के लिए एकल-अंक पूर्णांक ऑब्जेक्ट बनाएँ। यह।
इसके विपरीत, बाईं पाली को इस तरह से अनुकूलित नहीं किया जाता है, और प्रत्येक बाईं पारी में पूर्णांक को एक सरणी के रूप में स्थानांतरित किया जाता है। विशेष रूप से, यदि आप long_lshift()
एक छोटी लेकिन सकारात्मक बाईं पारी के मामले में स्रोत कोड को देखते हैं, तो 2-अंकीय पूर्णांक ऑब्जेक्ट हमेशा बनाया जाता है, यदि केवल इसकी लंबाई 1 बाद में कम हो गई है: (मेरी टिप्पणी /*** ***/
)
static PyObject *
long_lshift(PyObject *v, PyObject *w)
{
/*** ... ***/
wordshift = shiftby / PyLong_SHIFT; /*** zero for small w ***/
remshift = shiftby - wordshift * PyLong_SHIFT; /*** w for small w ***/
oldsize = Py_ABS(Py_SIZE(a)); /*** 1 for small v > 0 ***/
newsize = oldsize + wordshift;
if (remshift)
++newsize; /*** here newsize becomes at least 2 for w > 0, v > 0 ***/
z = _PyLong_New(newsize);
/*** ... ***/
}
पूर्णांक विभाजन
आपने सही बदलावों की तुलना में पूर्णांक मंजिल विभाजन के बदतर प्रदर्शन के बारे में नहीं पूछा, क्योंकि यह आपकी (और मेरी) अपेक्षाओं के अनुरूप है। लेकिन एक छोटे पॉजिटिव नंबर को दूसरे छोटे पॉजिटिव नंबर से विभाजित करना, छोटे गुणा के रूप में अनुकूलित नहीं है। प्रत्येक फ़ंक्शन का उपयोग करके //
भागफल और शेष दोनों की गणना करता है long_divrem()
। यह शेष गुणा के साथ एक छोटे भाजक के लिए गणना की जाती है , और एक नव-आवंटित पूर्णांक ऑब्जेक्ट में संग्रहीत किया जाता है , जो इस स्थिति में तुरंत खारिज कर दिया जाता है।
x
?