python - 为什么在 Python 3 中 x**4.0 比 x**4 快?

为什么 x**4.0x**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


TigerhawkT3指出它不会发生在循环之外。我检查了一下,这种情况仅在 base 上升时发生(据我所见)。有什么想法吗?

最佳答案

Why is x**4.0 faster than x**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); 

在哪里 iviw是我们原来的PyFloatObject s 为 C double秒。

For what it's worth: Python 2.7.13 for me is a factor 2~3 faster, and shows the inverse behaviour.

前面的事实也解释了 Python 2 和 3 之间的差异,所以我想我也应该解决这个评论,因为它很有趣。

在 Python 2 中,您使用的是旧的 intint 不同的对象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

请注意,虽然一个片段转换了 intlong而另一个没有(正如@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/

相关文章:

linux - 如何在 linux 上监控进程的线程数?

python - 如何更改绘图背景颜色?

python - 如何在python中声明零数组(或一定大小的数组)

python - 如何量化两个图像之间的差异?

linux - 创建 .tar.gz 文件时排除目录

python - 多处理:如何在类中定义的函数上使用 Pool.map?

linux - 抑制makefile中命令调用的回声?

linux - 遍历 ls -l 输出的每一行

c - 线程有不同的堆吗?

python - 在 SQLAlchemy 中使用 OR