[英]Why is 'x' in ('x',) faster than 'x' == 'x'?
[英]Why is x**4.0 faster than x**4 in Python 3?
為什么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
為什么
x**4.0
比Python 3 *中的x**4
快 ?
Python 3 int
對象是一個完全成熟的對象,旨在支持任意大小; 由於這個事實,它們在C級別上處理 (看看所有變量如何在long_pow
中聲明為PyLongObject *
類型)。 這也使得它們的取冪變得更加棘手和繁瑣,因為你需要使用它用來表示其值來執行它的ob_digit
數組。 ( 勇敢的來源。 - 請參閱: 了解更多關於PyLongObject
的Python中大整數的內存分配 。)
相反,Python float
對象可以轉換為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
是我們原來的PyFloatObject
如同C double
s。
值得一提的是:對我而言,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
s做的事情相同 , 當它執行取冪時它會被安全地轉換為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'>
long_pow
<type 'int'>
與<type 'int'>
long_pow
<type 'long'>
s相比有多緩慢,如果你在Python 2的long
調用中包裝x
名稱(基本上強制它在Python 3中使用long_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
請注意,雖然一個片段將int
轉換為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。 其他實現可能表現不同。
如果我們查看字節碼,我們可以看到表達式完全相同。 唯一的區別是一個常量類型,它將是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
,這個太長了,無法在這里粘貼,所以這里是鏈接 。
它調用x
的nb_power
槽,將y
作為參數傳遞。
最后,在Objects / floatobject.c的第686行的float_pow()
,我們看到在實際操作之前,參數被轉換為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);
...
因為一個是正確的,另一個是近似的。
>>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.