Python的编译器特意是简单的-
这使其变得快速且高度可预测。除了不断进行折叠之外,它基本上会生成忠实地模仿您的源代码的字节码。有人已经建议使用dis,这确实是查看所获得的字节码的好方法-例如,
for i in [1, 2, 3]:实际上是如何不进行常量折叠而是动态生成文字列表,而
for iin (1, 2, 3):(在文字元组上循环而不是文字列表) 是
能够固定倍(原因:一个列表是可变对象,并保持了“污垢简单的”任务书的编译器不会刻意去检查该特定列表从未如此修改它 可以 被优化成一个元组)。
因此,有足够的空间进行手动微优化-特别是吊装。即重写
for x in whatever(): anobj.amethod(x)
如
f = anobj.amethodfor x in whatever(): f(x)
保存重复的查找(编译器不会检查的运行是否
anobj.amethod实际上可以更改
anobj的绑定&c,以便下次需要进行新的查找-它只是做简单的事情,即无需吊装,这可以保证正确性,但绝对不能保证速度飞快;-)。
该timeit模块(在shell提示下恕我直言,最好使用),使得它非常简单的测量汇编+字节码解释的总体影响(只是确保片断你测量有没有副作用会影响时序,因为
timeit
不 跑过来并循环成一圈;-)。例如:
$ python -mtimeit 'for x in (1, 2, 3): pass'1000000 loops, best of 3: 0.219 usec per loop$ python -mtimeit 'for x in [1, 2, 3]: pass'1000000 loops, best of 3: 0.512 usec per loop
您可以看到重复列表构建的成本-并尝试进行一些细微调整来确认这确实是我们正在观察的结果:
$ python -mtimeit -s'Xs=[1,2,3]' 'for x in Xs: pass'1000000 loops, best of 3: 0.236 usec per loop$ python -mtimeit -s'Xs=(1,2,3)' 'for x in Xs: pass'1000000 loops, best of 3: 0.213 usec per loop
将iterable的构造移至
-s设置(仅运行一次且不定时)表明,在tuple上,适当的循环会稍快一些(可能是10%),但是第一对的问题就大了(列表比tuple慢了100%以上)
)主要与建筑相关。
手持
timeit和编译器是故意在其优化很简单的头脑的知识,我们可以很容易地回答你的其他问题:
以下操作的速度(相对而言)
* Function calls* Class instantiation* Arithmetic* 'Heavier' math operations such as sqrt()
$ python -mtimeit -s'def f(): pass' 'f()'10000000 loops, best of 3: 0.192 usec per loop$ python -mtimeit -s'class o: pass' 'o()'1000000 loops, best of 3: 0.315 usec per loop$ python -mtimeit -s'class n(object): pass' 'n()'10000000 loops, best of 3: 0.18 usec per loop
因此,我们看到:实例化一个新类并调用一个函数(均为空)的速度大致相同,实例化可能具有很小的速度余量,可能为5%;实例化一个老式类最慢(大约50%)。当然,微小差异(例如5%或更少)可能是噪音,因此建议重复尝试几次;但是像50%这样的差异绝对远远超出了噪音。
$ python -mtimeit -s'from math import sqrt' 'sqrt(1.2)'1000000 loops, best of 3: 0.22 usec per loop$ python -mtimeit '1.2**0.5'10000000 loops, best of 3: 0.0363 usec per loop$ python -mtimeit '1.2*0.5'10000000 loops, best of 3: 0.0407 usec per loop
在这里我们看到:调用
sqrt要比由操作员执行相同的计算(使用
**提高功率的操作员)慢,这要花大约调用空函数的代价;所有算术运算符在噪声范围内的速度大致相同(3或4纳秒的微小差异绝对是噪声;-)。检查连续折叠是否会干扰:
$ python -mtimeit '1.2*0.5'10000000 loops, best of 3: 0.0407 usec per loop$ python -mtimeit -s'a=1.2; b=0.5' 'a*b'10000000 loops, best of 3: 0.0965 usec per loop$ python -mtimeit -s'a=1.2; b=0.5' 'a*0.5'10000000 loops, best of 3: 0.0957 usec per loop$ python -mtimeit -s'a=1.2; b=0.5' '1.2*b'10000000 loops, best of 3: 0.0932 usec per loop
…我们发现确实是这样:如果将一个或两个数字作为变量(阻止不断折叠),我们将付出“现实”的代价。变量查找具有其自身的成本:
$ python -mtimeit -s'a=1.2; b=0.5' 'a'10000000 loops, best of 3: 0.039 usec per loop
无论如何,当我们试图测量如此微小的时刻时,这绝非微不足道。确实, 持续 查找也不是免费的:
$ python -mtimeit -s'a=1.2; b=0.5' '1.2'10000000 loops, best of 3: 0.0225 usec per loop
如您所见,虽然它比变量查找小,但却是相当可比的-大约一半。
如果(何时进行)(经过仔细的性能分析和测量)您决定对计算的某些核心进行迫切的优化,我建议您尝试cython-这是C
/
Python合并,它试图与Python一样简洁,与C一样快,而它无法做到100%肯定可以胜任(特别是,它使二进制代码比您的前代语言pyrex获得的速度要快得多,并且比它的功能还要丰富)
。对于最后几%的性能,您可能仍想使用C(在某些特殊情况下为汇编/机器代码),但这确实非常罕见。



