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
Run Code Online (Sandbox Code Playgroud)
我尝试改变我所提出的力量,看看它是如何动作的,例如,如果我将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);
Run Code Online (Sandbox Code Playgroud)
在哪里iv
和iw
我们的原始PyFloatObject
s为C double
s.
对于它的价值:Python
2.7.13
对我来说是一个2~3
更快的因素,并显示反向行为.
之前的事实也解释了Python 2和3之间的差异所以,我想我也会解决这个问题,因为它很有趣.
在Python 2中,您使用的int
是与int
Python 3中的对象不同的旧对象(int
3.x中的所有对象都是PyLongObject
类型).在Python 2中,有一个区别取决于对象的值(或者,如果使用后缀L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
Run Code Online (Sandbox Code Playgroud)
在<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 */
Run Code Online (Sandbox Code Playgroud)
这样可以获得良好的速度增益.
要看看<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
Run Code Online (Sandbox Code Playgroud)
请注意,虽然一个片段转换为int
to 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
Run Code Online (Sandbox Code Playgroud)
生成相同的字节代码'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
Run Code Online (Sandbox Code Playgroud)
所以时间是相同的.
*以上所有内容仅适用于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
Run Code Online (Sandbox Code Playgroud)
更新:让我们看一下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()");
}
Run Code Online (Sandbox Code Playgroud)
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);
...
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
16310 次 |
最近记录: |