Python性能的条件评估

时间:2012-10-30 13:58:40

标签: python performance

我试图找出在评估条件语句(python 2.6.6)时是否有否定布尔值的惩罚。我首先尝试了这个简单的测试(没有else分支)

>>> import timeit
>>> timeit.timeit("if not True: pass", number=100000)
0.011913061141967773
>>> timeit.timeit("if True: pass", number=100000)
0.018882036209106445

所以我虽然结果偏斜,因为pass语句可能被翻译成noop,至少是某些操作。

我做了第二次尝试并得到了这些结果:

>>> timeit.timeit("a=False\nif not a: pass\nelse: pass", number=100000)
0.02387714385986328
>>> timeit.timeit("a=False\nif a: pass\nelse: pass", number=100000)
0.015386819839477539
>>> timeit.timeit("a=True\nif a: pass\nelse: pass", number=100000)
0.02389812469482422
>>> timeit.timeit("a=True\nif not a: pass\nelse: pass", number=100000)
0.015424966812133789

我没想到会看到任何大的惩罚,但从这个结果看,评估else分支比隐式then分支便宜。差别很大!

所以第三次尝试返回这些结果:

>>> timeit.timeit("if True: a=1\nelse: a=1", number=100000)
0.022008895874023438
>>> timeit.timeit("if not True: a=1\nelse: a=1", number=100000)
0.022121906280517578

最后我得到了预期的结果。出于好奇,我最后一次尝试了:

>>> timeit.timeit("if False: a=1\nelse: a=1", number=100000)
0.02385997772216797
>>> timeit.timeit("if not False: a=1\nelse: a=1", number=100000)
0.02244400978088379

就是这样...... 我不知道为什么导致then分支的否定条件更快。

可能会发生什么?

所有这些结果都可以在我的计算机上重现,无论运行多少次都没关系,我得到的结果几乎相同。

我认为第一个测试偏差,因为编译器可能完全删除了else: pass部分。这可能吗?

这些结果可能与CPU中的分支预测器有关吗?

还有其他可能的罪魁祸首吗?

1 个答案:

答案 0 :(得分:16)

首先,“现实世界”的新闻:如果你真的处于写“如果没有”或“如果......通过其他......”会影响你的申请表现的情况,我建议你这样做一些严肃的分析,并在本机代码中重写你的内部循环 - 使用Cython或C(甚至其他选项,如Fortran - Python擅长集成)。

否则,我会说这些是不相关的实施细节。用“dis”反汇编代码序列可以显示正在发生的事情 - 但是在这个级别的优化在Python中没有任何实际价值 - 单个函数调用,即使是使用运算符隐含的函数调用,也需要额外的数量级。时间与if语句的执行时间相比。

下面:

>>> dis.dis(compile("if not True: pass", "None", "exec",))
  1           0 LOAD_NAME                0 (True)
              3 POP_JUMP_IF_TRUE         9
              6 JUMP_FORWARD             0 (to 9)
        >>    9 LOAD_CONST               0 (None)
             12 RETURN_VALUE        
>>> dis.dis(compile("if True: pass", "None", "exec",))
  1           0 LOAD_NAME                0 (True)
              3 POP_JUMP_IF_FALSE        9
              6 JUMP_FORWARD             0 (to 9)
        >>    9 LOAD_CONST               0 (None)
             12 RETURN_VALUE        
>>> 

正如您所看到的,生成的字节码没有区别 使用“not”或不使用 - 但对于跳跃运算符的op,在两种情况下应该花费相同的时间。