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

22 投票
1 回答
1479 浏览
提问于 2025-04-18 02:10

我知道这个问题可能有人会觉得没什么大不了,但我在高性能计算(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 个回答

29

因为窥视孔优化器通过预先计算乘法的结果来进行优化。

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在评论中指出的

它没有优化掉除法的原因是因为它的行为依赖于一些标志,比如 from __future__ import division,还有 -Q 标志。

引用Python 2.7.9中实际窥视孔优化器代码的评论

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

撰写回答