为什么x**4.0比Python3中的x**4快?

为什么x**4.0比Python3中的x**4快?,python,performance,python-3.x,python-3.5,python-internals,Python,Performance,Python 3.x,Python 3.5,Python Internals,为什么x**4.0比x**4快?我使用的是CPython3.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**4.0
x**4
快?我使用的是CPython3.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作为一个浮点数,它只会在24.1~4之间移动

我想这可能与浮点转换和2的幂有关,但我真的不知道

我注意到,在这两种情况下,2的幂更快,我想,因为这些计算对于解释器/计算机来说更为自然/容易。但是,随着浮子,它几乎没有移动
2.0=>24.1~4&128.0=>24.1~4
但是
2=>29&128=>62



指出它不会发生在循环之外。我检查了一下,这种情况只有在基础提升时才会发生(根据我所看到的)。你知道吗?

如果我们看字节码,我们可以看到表达式完全相同。唯一的区别是一种常量类型,它将是一个二元幂的参数。因此,这肯定是由于
int
被转换为一个浮点数

>>> def func(n):
...    return n**4
... 
>>> def func1(n):
...    return n**4.0
... 
>>> from dis import dis
>>> dis(func)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4)
              6 BINARY_POWER
              7 RETURN_VALUE
>>> dis(func1)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4.0)
              6 BINARY_POWER
              7 RETURN_VALUE

Update:让我们看一下cPython源代码:

PyObject*
PyNumber_电源(PyObject*v、PyObject*w、PyObject*z)
{
返回三值_op(v、w、z、NB_插槽(NB_电源),“**或pow()”;
}
PyNumber\u Power
调用
trialum\u op
,它太长,无法粘贴到此处,因此

它调用
x
nb\u power
插槽,将
y
作为参数传递

最后,在的第686行的
float_pow()
中,我们看到参数在实际操作之前被转换为C
double

静态PyObject*
浮点数(PyObject*v,PyObject*w,PyObject*z)
{
双iv、iw、ix;
int否定结果=0;
如果((PyObject*)z!=Py_None){
PyErr_SetString(PyExc_TypeError,“pow()第三个参数不是”
“允许,除非所有参数都是整数”);
返回NULL;
}
将_转换为_-DOUBLE(v,iv);
将_转换为_-DOUBLE(w,iw);
...
为什么Python 3*中的
x**4.0
x**4

Python3
int
对象是一个成熟的对象,旨在支持任意大小;因此,它们是(请参见如何将所有变量声明为
PyLongObject*
键入
long\u pow
)。这也使得它们的求幂变得更加复杂和乏味,因为您需要使用它用来表示其值的
ob_digit
数组来执行它。(--请参阅:有关
PyLongObject
s的详细信息。)

相反,Python
float
对象可以转换为C
double
类型(通过使用),并且可以执行操作。这非常好,因为在检查相关的边缘情况后,它允许Python()处理实际的求幂:

/*现在iv和iw是有限的,iw是非零的,iv是
*正且不等于1.0。我们最终允许
*平台可以介入并完成剩下的工作。
*/
errno=0;
PyFPE\u启动\u保护(“pow”,返回NULL)
ix=功率(iv,iw);
其中,
iv
iw
是我们原始的
PyFloatObject
s作为C
double
s

值得一提的是:Python
2.7.13
对我来说是一个更快的因素
2~3
,并且显示了相反的行为

前面的事实也解释了Python2和Python3之间的差异,因此,我认为我也应该处理这个评论,因为它很有趣

在Python2中,您使用的是与Python3中的
int
对象不同的旧
int
对象(3.x中的所有
int
对象都是
PyLongObject
类型)。在Python2中,有一个区别取决于对象的值(或者,如果使用后缀
L/L
):

这样可以获得良好的速度增益

要查看
s与
s相比有多慢,如果在Python2中将
x
名称包装在
long
调用中(基本上强制它像Python3中那样使用
long\u pow
),速度增益将消失:

# <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
'4**4.
生成相同的字节码,唯一的区别是
加载常量
加载浮点
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
所以时间是一样的



*以上所有内容仅适用于Python,Python的参考实现。其他实现的性能可能不同。

因为一个是正确的,另一个是近似的

>>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625

值得一提的是:Python 2.7.13对我来说快了2~3倍,并且表现出了相反的行为:整数指数比浮点指数快。@Evert yup,我得到了14个usec用于
x**4.0
和3.9用于
x**4
@Jean Françoisfare,我相信这是由于不断的折叠。我认为这意味着有一个相反的结果“最肯定”的是,他们的处理方式并没有什么不同在没有源代码的情况下有点牵强。@Mitch-特别是因为在这段代码中,这两个操作的执行时间没有差别。差别只出现在OP的循环中。这个答案很快就会得出结论。为什么你只看
float\u pow
,而这在慢的情况下甚至都不会运行?@TigerhawkT3:
4**4
4**4.0
得到常数折叠。这是一个完全不同的效果。不管它是什么,它都与
范围内的循环相关,因为只有计时
**
操作本身不会产生整数和浮点之间的差异。这种差异只有在查找变量时才会出现(
4**4
的速度与
4**4.0
一样快),这个答案根本没有涉及到这一点。但是,常数会被折叠
dis.dis(compile('4 ** 4.', '', 'exec'))
  1           0 LOAD_CONST               3 (256.0)
              2 POP_TOP
              4 LOAD_CONST               2 (None)
              6 RETURN_VALUE
>>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625