python中x与x_python - 为什么'x'中的'x'比'x'=='x'快?

正如我提到David Wolever所说的那样,除此之外还有更多的东西; 两种方法都发送到if (left_pointer == right_pointer); 你可以通过这样做证明这一点

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

第一个只能这么快,因为它按身份检查。

为了找出为什么人们需要比另一个更长的时间,让我们追溯执行。

它们都是从if (left_pointer == right_pointer)开始,从in开始,因为那是涉及的字节码

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);

if (left_pointer == right_pointer)是这样的:

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;

}

这是路径分裂的地方。 if (left_pointer == right_pointer)分支

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)

将采取和if (left_pointer == right_pointer),这是功能in,将采取。

这样做

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;

}

......等等,是不是if (left_pointer == right_pointer)其他分支采取了什么? 不,那是in。

那条代码路径很短,所以它可能只是降低了这两条代码的速度。 我们来比较吧。

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;

}

...

}

if (left_pointer == right_pointer)中的代码路径几乎立即终止。 对于in,它确实如此

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;

}

if (left_pointer == right_pointer)/in组合不在前面的路径中,但这些是相对较快的宏,在递增和递减某些全局变量后会短路。

if (left_pointer == right_pointer):

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;

...

}

...

}

这会做一些快速检查,拨打if (left_pointer == right_pointer)即可

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;

}

也就是说,这个快捷方式在if (left_pointer == right_pointer) ......但只是在做完之后

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() #

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() #

现在,if (left_pointer == right_pointer)和in相当便宜,因为它们只检查了几个字段,但显而易见的是,最上面的一个是较小的代码路径,它具有较少的函数调用,只有一个开关声明,只是有点薄。

TL; DR:

两者都发送到if (left_pointer == right_pointer); 不同之处在于他们为实现这一目标所做的工作量。 in只是做得少。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值