【问题标题】:Why `float` function is slower than multiplying by 1.0?为什么`float`函数比乘以1.0慢?
【发布时间】:2014-04-10 09:12:13
【问题描述】:

我知道这可能不是问题,但我为 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

【问题讨论】:

  • 除了两个 dict 查找(全局和内置)和一个函数调用的区别之外,第二个可能会应用 constant folding
  • 如果没有显示更接近结果的常量折叠(即当我用变量替换 98765 时),float 仍然稍微慢一些。至于为什么你肯定要使用float是像float('1.0')这样的统一转换。

标签: python optimization python-internals


【解决方案1】:

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

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 */

【讨论】:

  • 它没有优化除法的原因是它的行为取决于标志,比如from __future__ import division
  • @Davidmh 谢谢 :) 我也将其包含在答案中。
  • 如果你 a = 98765 然后测试 10/float(a)10/(a*1.0) 我仍然得到 2x 的速度差异,但编译后的代码似乎不受不断折叠的影响。
  • 我知道float()(调用__float__魔术方法)的目的与乘以1.0有很大不同,但我经常看到python 2整数除法被教为a/float(b ) (或from __future__ import division),我试图找出除了float() 能够处理具有__float__ 方法的任意类型之外的原因
  • @JasonP 该划分可以通过程序中未设置的标志来控制。这就是为什么,它没有优化它。
猜你喜欢
  • 1970-01-01
  • 2018-10-17
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 2014-02-09
相关资源
最近更新 更多