以下是翻译内容:
为什么在Python 3中,x**4.0
比x**4
更快*?
Python 3的int
对象是一个完整的对象,旨在支持任意大小;由于这个事实,它们在C级别上被视为这样处理(请参见long_pow
中声明所有变量都是PyLongObject *
类型)。这也使得它们的指数运算更加{{em>棘手和繁琐,因为你需要在使用ob_digit
数组表示其值时进行调整。(勇敢者可参考源代码。 -- 有关PyLongObject
的更多信息,请参见了解Python中大整数内存分配。)
Python的float
对象,相反地,可以通过使用PyFloat_AsDouble
将其转换为C double
类型,并且可以使用这些本机类型执行操作。这非常好,因为在检查相关边缘情况后,它允许Python 使用平台的pow
(也就是C的pow
)来处理实际的指数运算:
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw);
其中iv
和iw
是我们原始的PyFloatObject
,作为C中的double
。
值得一提的是:对我而言,Python 2.7.13
更快了2~3倍,并显示出相反的行为。
前面的事实也解释了Python 2和3之间的差异,因此,我认为这个评论也很有趣,需要解释一下。
在Python 2中,您使用的是旧的int
对象,它与Python 3中的int
对象不同(所有3.x中的int
对象都是PyLongObject
类型)。在Python 2中,这取决于对象的值(或者,如果使用后缀L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
你在这里看到的<type 'int'>
与float
做相同的事情,当进行指数运算时,它会被安全地转换为C语言中的long
(如果可以的话,int_pow
还会提示编译器将其放入寄存器中,这可能会有所不同):
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
这可以获得很好的速度提升。
为了看到在比较中
<type 'long'>
与
<type 'int'>
的迟缓程度,如果你在Python 2中使用
long
调用来包装
x
名称(实质上强制它使用像Python 3中的
long_pow
),速度增益就会消失:
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"
10000 loops, best of 3: 116 usec per loop
(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 指出的那样),但这种转换并不是减速的主要原因。实现
long_pow
的方式才是。(只使用
long(x)
来计时语句以查看结果)。
[...] 它不会在循环之外发生。[...] 有任何想法吗?
这是 CPython 的 peephole 优化器为您折叠常量。由于没有实际计算来找到指数运算的结果,只有值的加载,因此两种情况下的时间完全相同。
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
加载的是浮点数256.0
而不是整数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
所以时间是相同的。
*以上所有内容仅适用于Python的参考实现CPython。其他实现可能表现不同。
x**4.0
我得到了14微秒,而x**4
得到了3.9微秒。 - dabadaba