David Wolever'a bahsettiğim gibi, bununla karşılaşmaktan daha fazlası var; her iki yöntem de gönderilir is
; bunu yaparak kanıtlayabilirsin
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
Birincisi çok hızlı olabilir, çünkü kimlikle kontrol eder.
Birinin neden diğerinden daha uzun süreceğini öğrenmek için, yürütmeyi izleyelim.
İkisi de başlar ceval.c
, COMPARE_OP
çünkü bu ilgili bayt kodu
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();
}
Bu, değerleri yığından çıkar (teknik olarak yalnızca bir tane çıkar)
PyObject *right = POP();
PyObject *left = TOP();
ve karşılaştırmayı çalıştırır:
PyObject *res = cmp_outcome(oparg, left, right);
cmp_outcome
bu:
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;
}
Burası yolların ayrıldığı yerdir. PyCmp_IN
şube yapar
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);
}
Bir demetin şu şekilde tanımlandığını unutmayın:
static PySequenceMethods tuple_as_sequence = {
...
(objobjproc)tuplecontains, /* sq_contains */
};
PyTypeObject PyTuple_Type = {
...
&tuple_as_sequence, /* tp_as_sequence */
...
};
Böylece şube
if (sqm != NULL && sqm->sq_contains != NULL)
alınacak ve *sqm->sq_contains
bu işlev olan (objobjproc)tuplecontains
alınacaktır.
Bu yapar
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;
}
... Bekle, PyObject_RichCompareBool
diğer dal bu değil miydi? Hayır, öyleydi PyObject_RichCompare
.
Bu kod yolu kısaydı, bu yüzden muhtemelen bu ikisinin hızına iniyor. Hadi karşılaştıralım.
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;
}
...
}
Kod yolu PyObject_RichCompareBool
hemen hemen sona erer. Çünkü PyObject_RichCompare
öyle
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
Kombo önceki yolunda alınmadığı, ancak bu nispeten hızlı makrolardır olduğunu olacak artırma ve bazı globalsi eksiltim sonra kısa devre.
do_richcompare
yapar:
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;
...
}
...
}
Bu çağrıya bazı hızlı kontroller yapar v->ob_type->tp_richcompare
olduğunu
PyTypeObject PyUnicode_Type = {
...
PyUnicode_RichCompare, /* tp_richcompare */
...
};
hangisi
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;
}
Yani, bu kısayollar left == right
... ama sadece
if (!PyUnicode_Check(left) || !PyUnicode_Check(right))
if (PyUnicode_READY(left) == -1 ||
PyUnicode_READY(right) == -1)
Tüm yollarda daha sonra böyle bir şeye benziyor (bilinen dalları manuel olarak özyinelemeli olarak satır içi, açma ve budama)
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() #
vs
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() #
Şimdi PyUnicode_Check
vePyUnicode_READY
sadece birkaç alanı kontrol ettikleri için oldukça ucuz, ancak en üstteki kodun daha küçük bir kod yolu olduğu, daha az fonksiyon çağrısı, sadece bir anahtar ifadesi ve sadece biraz daha ince olduğu açık olmalıdır.
TL; DR:
Her ikisi de if (left_pointer == right_pointer)
; fark, oraya ulaşmak için ne kadar iş yaptıklarıdır. in
sadece daha azını yapar.
in
bunun yerine her yerde kullanmaya başlamayın==
. Okunabilirliğe zarar veren erken bir optimizasyon.