जैसा कि मैंने डेविड वोलेवर का उल्लेख किया है, आँख से मिलने की तुलना में यह अधिक है; दोनों तरीकों से प्रेषण is
; आप ऐसा करके साबित कर सकते हैं
min(Timer("x == x", setup="x = 'a' * 1000000").repeat(10, 10000))
#>>> 0.00045456900261342525
min(Timer("x == y", setup="x = 'a' * 1000000; y = 'a' * 1000000").repeat(10, 10000))
#>>> 0.5256857610074803
पहला केवल इतना तेज़ हो सकता है क्योंकि यह पहचान से जाँच करता है।
यह पता लगाने के लिए कि कोई दूसरे से अधिक समय क्यों लेगा, आइए निष्पादन के माध्यम से ट्रेस करें।
वे दोनों अंदर से शुरू करते हैं ceval.c
, COMPARE_OP
जिसमें से बाईटकोड शामिल है
TARGET(COMPARE_OP) {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *res = cmp_outcome(oparg, left, right);
Py_DECREF(left);
Py_DECREF(right);
SET_TOP(res);
if (res == NULL)
goto error;
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
DISPATCH();
}
यह स्टैक से मानों को पॉप करता है (तकनीकी रूप से यह केवल एक पॉप करता है)
PyObject *right = POP();
PyObject *left = TOP();
और तुलना चलाता है:
PyObject *res = cmp_outcome(oparg, left, right);
cmp_outcome
क्या यह:
static PyObject *
cmp_outcome(int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS: ...
case PyCmp_IS_NOT: ...
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
case PyCmp_NOT_IN: ...
case PyCmp_EXC_MATCH: ...
default:
return PyObject_RichCompare(v, w, op);
}
v = res ? Py_True : Py_False;
Py_INCREF(v);
return v;
}
यहीं से रास्ते बिखर जाते हैं। PyCmp_IN
शाखा करता है
int
PySequence_Contains(PyObject *seq, PyObject *ob)
{
Py_ssize_t result;
PySequenceMethods *sqm = seq->ob_type->tp_as_sequence;
if (sqm != NULL && sqm->sq_contains != NULL)
return (*sqm->sq_contains)(seq, ob);
result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS);
return Py_SAFE_DOWNCAST(result, Py_ssize_t, int);
}
ध्यान दें कि एक टपल को परिभाषित किया गया है
static PySequenceMethods tuple_as_sequence = {
...
(objobjproc)tuplecontains, /* sq_contains */
};
PyTypeObject PyTuple_Type = {
...
&tuple_as_sequence, /* tp_as_sequence */
...
};
तो शाखा
if (sqm != NULL && sqm->sq_contains != NULL)
लिया जाएगा और *sqm->sq_contains
, जो कार्य है (objobjproc)tuplecontains
, लिया जाएगा।
यह करता है
static int
tuplecontains(PyTupleObject *a, PyObject *el)
{
Py_ssize_t i;
int cmp;
for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
cmp = PyObject_RichCompareBool(el, PyTuple_GET_ITEM(a, i),
Py_EQ);
return cmp;
}
... रुको, यह नहीं था कि PyObject_RichCompareBool
दूसरी शाखा ने क्या लिया? नहीं, कि था PyObject_RichCompare
।
यह कोड पथ छोटा था इसलिए यह संभवत: बस इन दोनों की गति के नीचे आ गया। आइए तुलना करते हैं।
int
PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
{
PyObject *res;
int ok;
/* Quick result when objects are the same.
Guarantees that identity implies equality. */
if (v == w) {
if (op == Py_EQ)
return 1;
else if (op == Py_NE)
return 0;
}
...
}
PyObject_RichCompareBool
बहुत ज्यादा तुरंत कोड कोड समाप्त हो जाता है। के लिए PyObject_RichCompare
, यह करता है
PyObject *
PyObject_RichCompare(PyObject *v, PyObject *w, int op)
{
PyObject *res;
assert(Py_LT <= op && op <= Py_GE);
if (v == NULL || w == NULL) { ... }
if (Py_EnterRecursiveCall(" in comparison"))
return NULL;
res = do_richcompare(v, w, op);
Py_LeaveRecursiveCall();
return res;
}
Py_EnterRecursiveCall
/ Py_LeaveRecursiveCall
कॉम्बो पिछले रास्ते में नहीं रखा जाता है, लेकिन इन अपेक्षाकृत जल्दी मैक्रो हैं कि हूँ incrementing और कुछ वैश्विक decrementing के बाद शॉर्ट सर्किट।
do_richcompare
कर देता है:
static PyObject *
do_richcompare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
if (v->ob_type != w->ob_type && ...) { ... }
if ((f = v->ob_type->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
...
}
...
}
यह कॉल करने के लिए कुछ त्वरित जांच करता v->ob_type->tp_richcompare
है
PyTypeObject PyUnicode_Type = {
...
PyUnicode_RichCompare, /* tp_richcompare */
...
};
जो करता है
PyObject *
PyUnicode_RichCompare(PyObject *left, PyObject *right, int op)
{
int result;
PyObject *v;
if (!PyUnicode_Check(left) || !PyUnicode_Check(right))
Py_RETURN_NOTIMPLEMENTED;
if (PyUnicode_READY(left) == -1 ||
PyUnicode_READY(right) == -1)
return NULL;
if (left == right) {
switch (op) {
case Py_EQ:
case Py_LE:
case Py_GE:
/* a string is equal to itself */
v = Py_True;
break;
case Py_NE:
case Py_LT:
case Py_GT:
v = Py_False;
break;
default:
...
}
}
else if (...) { ... }
else { ...}
Py_INCREF(v);
return v;
}
अर्थात्, यह शॉर्टकट left == right
... पर केवल करने के बाद
if (!PyUnicode_Check(left) || !PyUnicode_Check(right))
if (PyUnicode_READY(left) == -1 ||
PyUnicode_READY(right) == -1)
सभी रास्तों में सभी कुछ इस तरह से देखते हैं (मैन्युअल रूप से पुनरावर्ती inlining, unrolling और pruning ज्ञात शाखाएं)
POP() # Stack stuff
TOP() #
#
case PyCmp_IN: # Dispatch on operation
#
sqm != NULL # Dispatch to builtin op
sqm->sq_contains != NULL #
*sqm->sq_contains #
#
cmp == 0 # Do comparison in loop
i < Py_SIZE(a) #
v == w #
op == Py_EQ #
++i #
cmp == 0 #
#
res < 0 # Convert to Python-space
res ? Py_True : Py_False #
Py_INCREF(v) #
#
Py_DECREF(left) # Stack stuff
Py_DECREF(right) #
SET_TOP(res) #
res == NULL #
DISPATCH() #
बनाम
POP() # Stack stuff
TOP() #
#
default: # Dispatch on operation
#
Py_LT <= op # Checking operation
op <= Py_GE #
v == NULL #
w == NULL #
Py_EnterRecursiveCall(...) # Recursive check
#
v->ob_type != w->ob_type # More operation checks
f = v->ob_type->tp_richcompare # Dispatch to builtin op
f != NULL #
#
!PyUnicode_Check(left) # ...More checks
!PyUnicode_Check(right)) #
PyUnicode_READY(left) == -1 #
PyUnicode_READY(right) == -1 #
left == right # Finally, doing comparison
case Py_EQ: # Immediately short circuit
Py_INCREF(v); #
#
res != Py_NotImplemented #
#
Py_LeaveRecursiveCall() # Recursive check
#
Py_DECREF(left) # Stack stuff
Py_DECREF(right) #
SET_TOP(res) #
res == NULL #
DISPATCH() #
अब, PyUnicode_Check
और PyUnicode_READY
बहुत सस्ते हैं क्योंकि वे केवल कुछ क्षेत्रों की जांच करते हैं, लेकिन यह स्पष्ट होना चाहिए कि शीर्ष एक छोटा कोड पथ है, इसमें कम फ़ंक्शन कॉल हैं, केवल एक स्विच स्टेटमेंट है और बस थोड़ा पतला है।
टी एल; डॉ:
दोनों को प्रेषण if (left_pointer == right_pointer)
; फर्क सिर्फ इतना है कि वे वहां पहुंचने के लिए कितना काम करते हैं। in
बस कम करता है।
in
बजाय हर जगह का उपयोग शुरू मत करो==
। यह एक समयपूर्व अनुकूलन है जो पठनीयता को हानि पहुँचाता है।