为什么`float`函数慢于乘以1.0?

时间:2014-04-10 09:12:13

标签: python optimization python-internals

据我所知,这可以说是一个非问题,但是我为HPC环境编写了软件,所以这个3.5倍的速度实际上有所作为。

In [1]: %timeit 10 / float(98765)            
1000000 loops, best of 3: 313 ns per loop

In [2]: %timeit 10 / (98765 * 1.0)
10000000 loops, best of 3: 80.6 ns per loop

我使用dis来查看代码,我认为float()会因为需要调用函数而变慢(遗憾的是我无法dis.dis(float)看到它实际上在做什么。)

我想第二个问题是我应该何时使用float(n),何时应该使用n * 1.0

1 个答案:

答案 0 :(得分:28)

因为Peep孔优化器通过预先计算乘法结果来优化它

import dis
dis.dis(compile("10 / float(98765)", "<string>", "eval"))

  1           0 LOAD_CONST               0 (10)
              3 LOAD_NAME                0 (float)
              6 LOAD_CONST               1 (98765)
              9 CALL_FUNCTION            1
             12 BINARY_DIVIDE       
             13 RETURN_VALUE        

dis.dis(compile("10 / (98765 * 1.0)", "<string>", "eval"))

  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               3 (98765.0)
              6 BINARY_DIVIDE       
              7 RETURN_VALUE        

它将98765 * 1.0的结果存储在字节代码中作为常量值。因此,它只需要加载它并进行除法,就像在第一种情况下我们必须调用函数一样。

我们可以更清楚地看到这个

print compile("10 / (98765 * 1.0)", "<string>", "eval").co_consts
# (10, 98765, 1.0, 98765.0)

由于该值是在编译时自己预先计算的,所以第二个更快。

修改:正如Davidmh in the comments指出的那样,

  

它之所以也没有优化掉除法的原因是因为它的行为取决于标志,例如from __future__ import division,也因为-Q标志。

引用the comment from the actual peephole optimizer code for Python 2.7.9

        /* Cannot fold this operation statically since
           the result can depend on the run-time presence
           of the -Qnew flag */