ari*_*nai 163 python performance python-3.x python-internals python-3.5
为什么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提升为浮动,它只是移动大约24.1~4.
我想它可能与浮点转换和2的幂有关,但我真的不知道.
我注意到在两种情况下,2的幂都更快,我想因为这些计算对于解释器/计算机而言更加原生/容易.但是,对于浮子,它几乎没有移动.2.0 => 24.1~4 & 128.0 => 24.1~4 但 2 => 29 & 128 => 62
Jim*_*ard 160
为什么比Python 3 *
x**4.0更快?x**4
Python 3 int对象是一个完全成熟的对象,旨在支持任意大小; 由于这个事实,它们在C级别上处理(请参见如何将所有变量声明为PyLongObject *类型long_pow).这也使得它们的取幂变得更加棘手和繁琐,因为你需要ob_digit使用它用来表示它执行它的值的数组.(勇敢的来源. - 请参阅:了解更多关于Python中大整数的内存分配PyLongObject.)
float相反,Python 对象可以转换为C double类型(通过使用PyFloat_AsDouble),并且可以使用这些本机类型执行操作.这很好,因为在检查相关的边缘情况之后,它允许Python 使用平台'pow(C' pow,即)来处理实际的取幂:
/* 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我们的原始PyFloatObjects为C doubles.
对于它的价值:Python
2.7.13对我来说是一个2~3更快的因素,并显示反向行为.
之前的事实也解释了Python 2和3之间的差异所以,我想我也会解决这个问题,因为它很有趣.
在Python 2中,您使用的int是与intPython 3中的对象不同的旧对象(int3.x中的所有对象都是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;
/* Snipped for brevity */    
这样可以获得良好的速度增益.
要看看<type 'long'>与<type 'int'>s 相比有多缓慢,如果你在Python 2 x的long调用中包装了这个名字(基本上强制它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
请注意,虽然一个片段转换为intto long而另一个片段没有(正如@pydsinger所指出的),这个演员阵容并不是减速背后的贡献力量.执行long_pow是.(仅用陈述时间陈述long(x)).
[...]它不会发生在循环之外.[...]任何想法?
这是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
所以时间是相同的.
*以上所有内容仅适用于Python的参考实现CPython.其他实现可能表现不同.
leo*_*ovp 25
如果我们查看字节码,我们可以看到表达式完全相同.唯一的区别是一个常量的类型,它将成为一个参数BINARY_POWER.所以它肯定是由于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
更新:让我们看一下CPython源代码中的Objects/abstract.c:
PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
{
    return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
}
PyNumber_Power电话ternary_op,这太长了,无法粘贴在这里,所以这里是链接.
它调用nb_power槽作为参数x传递y.
最后,在Objects/floatobject.c的float_pow()第686行,我们看到在实际操作之前,参数被转换为C :double
static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
    double iv, iw, ix;
    int negate_result = 0;
    if ((PyObject *)z != Py_None) {
        PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not "
            "allowed unless all arguments are integers");
        return NULL;
    }
    CONVERT_TO_DOUBLE(v, iv);
    CONVERT_TO_DOUBLE(w, iw);
    ...