যেমনটি আমি ডেভিড ওলবারকে বলেছি, চোখের সাক্ষাতের চেয়ে আরও কিছু আছে; উভয় পদ্ধতি প্রেরণ 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);
}
নোট করুন যে একটি tuple হিসাবে সংজ্ঞায়িত করা হয়
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
কম্বো পূর্ববর্তী পথে নিয়ে যাওয়া হয় না, কিন্তু এই অপেক্ষাকৃত দ্রুত ম্যাক্রো আছে হবে বৃদ্ধিশীল এবং কিছু globals 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)
সমস্ত পাথের সমস্তটিতে এর পরে এমন কিছু চেহারা (ম্যানুয়ালি পুনরাবৃত্তভাবে ইনলাইনিং, তালিকাবদ্ধকরণ এবং পরিচিত শাখাগুলি ছাঁটাই করা)
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
পরিবর্তে সর্বত্র ব্যবহার শুরু করবেন না==
। এটি অকাল অপটিমাইজেশন যা পাঠযোগ্যতার ক্ষতি করে।