为什么'4 ** 4.'比Python 3 *中的LOAD_CONST更快?

Python 3 '4 ** 4.'对象是一个完全成熟的对象,旨在支持任意大小; 由于这个事实,它们在C级别上处理(请参见256.0中所有变量如何声明为LOAD_CONST类型)。 这也使得它们的取幂更加棘手和繁琐,因为你需要使用它来表示它执行它的值的256数组。 (勇敢的来源。 - 请参阅:了解更多关于long_pows的Python中大整数的内存分配。)

相反,Python '4 ** 4.'对象可以转换为C LOAD_CONST类型(通过使用'4 ** 4.'),并且可以使用这些本机类型执行操作。 这很好,因为在检查相关边缘情况后,它允许Python使用平台''4 ** 4.'(即C的'4 ** 4.',即)来处理实际的取幂:

/* 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);

其中'4 ** 4.'和LOAD_CONST是我们的原始256.0s为C 256s。

对于它的价值:Python '4 ** 4.'对我来说是一个因子LOAD_CONST更快,并显示反向行为。

之前的事实也解释了Python 2和3之间的差异所以,我想我也会解决这个问题,因为它很有趣。

在Python 2中,您使用的旧对象与Python 3中的LOAD_CONST对象不同(3.x中的所有256.0对象均为256类型)。 在Python 2中,有一个区别取决于对象的值(或者,如果使用后缀long_pow):

# Python 2
type(30) # 
type(30L) # 
您在这里看到的'4 ** 4.'执行相同的操作'4 ** 4.'s,当它执行取幂时它会被安全地转换为C LOAD_CONST(256.0也暗示编译器将它们放入寄存器中,如果它可以这样做,那么可以使 区别):
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
这样可以获得良好的速度增益。
要查看'4 ** 4.'与LOAD_CONST相比有多缓慢,如果您在Python 2中的256调用中包含256.0名称(基本上强制它在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
需要注意的是,虽然一个片段将'4 ** 4.'转换为LOAD_CONST而另一个片段没有(正如@pydsinger所指出的那样),但此演员阵容并不是减速背后的贡献力量。 执行256.0是。 (单独用256查看陈述时间)。
[...]它不会发生在循环之外。 [...]任何想法?
这是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加载浮点数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

所以时间是相同的。