我正在使用以下代码进行测试,它似乎<那个> =。慢,有人知道为什么吗?
import timeit
s = """
x=5
if x<0: pass
"""
t = timeit.Timer(stmt=s)
print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
#0.21 usec/pass
z = """
x=5
if x>=0: pass
"""
t2 = timeit.Timer(stmt=z)
print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
#0.18 usec/pass
答案 0 :(得分:32)
在Python 3.1.2中,有时&lt;比&gt; =更快。我尝试在反汇编程序中读取它,
import dis
def f1():
x=5
if x < 0: pass
def f2():
x = 5
if x >=0: pass
>>> dis.dis(f1)
2 0 LOAD_CONST 1 (5)
3 STORE_FAST 0 (x)
3 6 LOAD_FAST 0 (x)
9 LOAD_CONST 2 (0)
12 COMPARE_OP 0 (<)
15 POP_JUMP_IF_FALSE 21
18 JUMP_FORWARD 0 (to 21)
>> 21 LOAD_CONST 0 (None)
24 RETURN_VALUE
>>> dis.dis(f2)
2 0 LOAD_CONST 1 (5)
3 STORE_FAST 0 (x)
3 6 LOAD_FAST 0 (x)
9 LOAD_CONST 2 (0)
12 COMPARE_OP 5 (>=)
15 POP_JUMP_IF_FALSE 21
18 JUMP_FORWARD 0 (to 21)
>> 21 LOAD_CONST 0 (None)
24 RETURN_VALUE
代码几乎相同,但f1始终在第15行运行并跳转到21,f2始终运行15 - &gt; 18 - &gt; 21,因此在if语句中性能应该受到true / false的影响而不是&lt;或&gt; =问题。
答案 1 :(得分:5)
您的第一个测试评估为true,第二个测试评估为false。也许结果处理略有不同。
答案 2 :(得分:5)
COMPARE_OP操作码包含一个优化,其中两个操作数都是与C兼容的整数,在这种情况下,它只是与比较类型的switch语句进行内联比较:
if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
/* INLINE: cmp(int, int) */
register long a, b;
register int res;
a = PyInt_AS_LONG(v);
b = PyInt_AS_LONG(w);
switch (oparg) {
case PyCmp_LT: res = a < b; break;
case PyCmp_LE: res = a <= b; break;
case PyCmp_EQ: res = a == b; break;
case PyCmp_NE: res = a != b; break;
case PyCmp_GT: res = a > b; break;
case PyCmp_GE: res = a >= b; break;
case PyCmp_IS: res = v == w; break;
case PyCmp_IS_NOT: res = v != w; break;
default: goto slow_compare;
}
x = res ? Py_True : Py_False;
Py_INCREF(x);
}
因此,比较中唯一的变化是通过switch语句的路由以及结果是True还是False。我的猜测是你只是看到由于CPU的执行路径(也许是分支预测)引起的变化,所以你看到的效果可能很容易消失,或者在其他版本的Python中反过来。
答案 3 :(得分:2)
我刚刚在Python 3.1.2中尝试过它 - 没有区别。
编辑:经过多次重复次数的尝试后,我发现这两个版本的值差异很大:
>>> import timeit
>>> s = """x=5
... if x<0: pass"""
>>> t = timeit.Timer(stmt=s)
>>> print ("%.2f usec/pass" % (1000000 * t.timeit(number=1000000)/100000))
1.48 usec/pass
>>>
>>> z = """x=5
... if x>=0: pass"""
>>> t2 = timeit.Timer(stmt=z)
>>> print ("%.2f usec/pass" % (1000000 * t.timeit(number=1000000)/100000))
0.59 usec/pass
>>>
>>> import timeit
>>> s = """x=5
... if x<0: pass"""
>>> t = timeit.Timer(stmt=s)
>>> print ("%.2f usec/pass" % (1000000 * t.timeit(number=1000000)/100000))
0.57 usec/pass
>>>
>>> z = """x=5
... if x>=0: pass"""
>>> t2 = timeit.Timer(stmt=z)
>>> print ("%.2f usec/pass" % (1000000 * t.timeit(number=1000000)/100000))
1.47 usec/pass
所以我猜这里与其他进程的调度冲突是这里的主要变量。
答案 4 :(得分:2)
对于某些激活它的“timeit”似乎有一些固有的开销(出乎意料的足够)。
尝试 -
import timeit
Times = 30000000
s = """
x=5
if x>=0: pass
"""
t1 = timeit.Timer( stmt=s )
t2 = timeit.Timer( stmt=s )
t3 = timeit.Timer( stmt=s )
print t1.timeit( number=Times )
print t2.timeit( number=Times )
print t3.timeit( number=Times )
print t1.timeit( number=Times )
print t2.timeit( number=Times )
print t3.timeit( number=Times )
print t1.timeit( number=Times )
print t2.timeit( number=Times )
print t3.timeit( number=Times )
print t1.timeit( number=Times )
print t2.timeit( number=Times )
print t3.timeit( number=Times )
在我的机器上输出是(一致的,无论我尝试多少循环 - 所以它可能不会恰好与机器上发生的其他事情重合) -
1.96510925271
1.84014169399
1.84004224001
1.97851123537
1.86845451028
1.83624929984
1.94599509155
1.85690220405
1.8338135154
1.98382475985
1.86861430713
1.86006657271
't1'总是需要更长时间。 但是如果你试图对调用或对象创建进行重新排序,那么事情就会有所不同(而不是我可以轻易解释的模式)。
这不是你问题的答案,只是观察到以这种方式测量可能会产生固有的不准确性。
答案 5 :(得分:1)
有趣!如果简化表达式,结果会更加强调
使用IPython我发现x<=0
需要150ns而x<0
需要320ns - 超过两倍
其他比较x>0
和x>=0
似乎也需要300左右
即使超过1000000次循环,结果也会波动很多,但
答案 6 :(得分:1)
这是一个相当有趣的问题。我使用if cond: pass
删除了v=cond
,但并没有完全消除差异。我仍然不确定答案,但我找到了一个合理的理由:
switch (op) {
case Py_LT: c = c < 0; break;
case Py_LE: c = c <= 0; break;
case Py_EQ: c = c == 0; break;
case Py_NE: c = c != 0; break;
case Py_GT: c = c > 0; break;
case Py_GE: c = c >= 0; break;
}
这是来自Objects / object.c funcion convert_3way_to_object。请注意,&gt; =是最后一个分支;这意味着它一个人不需要退出跳跃。那个中断声明被消除了。它与shiki的反汇编中的0和5匹配。作为无条件中断,它可以通过分支预测来处理,但也可能导致更少的代码加载。
在这个级别,差异自然是高度机器特定的。我的测量结果不是很彻底,但这是C级的一点,我看到了操作员之间的偏见。我可能从CPU速度缩放中获得了更大的偏差。