为什么 `float` 函数比乘以 1.0 慢?
我知道这个问题可能有人会觉得没什么大不了,但我在高性能计算(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
标志。
/* Cannot fold this operation statically since
the result can depend on the run-time presence
of the -Qnew flag */