为什么 x**4.0
比 x**4
快?我正在使用 CPython 3.5.2。
$ python -m timeit "for x in range(100):" " x**4.0"
10000 loops, best of 3: 24.2 usec per loop
$ python -m timeit "for x in range(100):" " x**4"
10000 loops, best of 3: 30.6 usec per loop
我尝试改变我提高的力量,看看它是如何起作用的,例如,如果我将 x 提高到 10 或 16 的幂,它会从 30 跳到 35,但如果我提高 10.0 作为一个 float ,它只是在 24.1~4 左右移动。
我猜这可能与浮点转换和 2 的幂有关,但我真的不知道。
我注意到在这两种情况下 2 的幂都更快,我猜是因为这些计算对于解释器/计算机来说更原生/更容易。但是,对于 float ,它几乎不会移动。 2.0 => 24.1~4 & 128.0 => 24.1~4
但是 2 => 29 & 128 => 62
最佳答案
Why is
x**4.0
faster thanx**4
in Python 3*?
Python 3 int
对象是一个完整的对象,旨在支持任意大小;由于这个事实,他们是handled as such on the C level (查看如何将所有变量声明为 PyLongObject *
输入 long_pow
)。这也使得它们的求幂变得更加棘手和乏味,因为您需要使用 ob_digit
它用来表示执行它的值的数组。 (Source for the brave. -- 参见:Understanding memory allocation for large integers in Python 了解更多关于 PyLongObject
的信息)
Python float
相反,对象可以转换为 C double
类型(通过使用 PyFloat_AsDouble
)并且可以执行操作 using those native types . 这很棒 因为在检查了相关的边缘情况之后,它允许 Python use the platforms' pow
( C's pow
, that is ) 来处理实际的求幂:
/* Now iv and iw are finite, iw is nonzero, and iv is
* positive and not equal to 1.0. We finally allow
* the platform pow to step in and do the rest.
*/
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw);
在哪里 iv
和 iw
是我们原来的PyFloatObject
s 为 C double
秒。
For what it's worth: Python
2.7.13
for me is a factor2~3
faster, and shows the inverse behaviour.
前面的事实也解释了 Python 2 和 3 之间的差异,所以我想我也应该解决这个评论,因为它很有趣。
在 Python 2 中,您使用的是旧的 int
与 int
不同的对象Python 3 中的对象(所有 int
3.x 中的对象都是 PyLongObject
类型)。在 Python 2 中,有一个取决于对象的值的区别(或者,如果您使用后缀 L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
<type 'int'>
你在这里看到做同样的事情float
s 这样做,它被安全地转换为 C long
when exponentiation is performed on it (int_pow
还提示编译器如果可以的话将它们放入寄存器中,以便 可以 有所作为):
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
这样可以获得良好的速度增益。
看看有多呆滞<type 'long'>
s 与 <type 'int'>
进行比较s,如果你包裹了 x
long
中的名称在 Python 2 中调用(本质上是强制它使用 long_pow
,就像在 Python 3 中一样),速度增益消失了:
# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"
10000 loops, best of 3: 116 usec per loop
# <type 'long'>
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop
请注意,虽然一个片段转换了 int
至long
而另一个没有(正如@pydsinger 所指出的那样),这个 Actor 并不是经济放缓背后的插入力。 long_pow
的执行是。 (仅用 long(x)
对语句计时以查看)。
[...] it doesn't happen outside of the loop. [...] Any idea about that?
这是 CPython 的窥孔优化器为您折叠常量。无论哪种情况,您都会得到相同的确切时间,因为没有实际计算来找到求幂的结果,只加载值:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
为 '4 ** 4.'
生成相同的字节码唯一的区别是 LOAD_CONST
加载 float 256.0
而不是 int 256
:
dis.dis(compile('4 ** 4.', '', 'exec'))
1 0 LOAD_CONST 3 (256.0)
2 POP_TOP
4 LOAD_CONST 2 (None)
6 RETURN_VALUE
所以时间是相同的。
*以上所有内容仅适用于 CPython,即 Python 的引用实现。其他实现可能会有不同的表现。
https://stackoverflow.com/questions/42355194/