簡體   English   中英

為什么x ** 4.0比Python 3中的x ** 4快?

[英]Why is x**4.0 faster than x**4 in Python 3?

為什么x**4.0x**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


TigerhawkT3指出它不會發生在循環之外。 我檢查過,當基地升起時,情況才會發生(從我所看到的情況)。 有什么想法嗎?

為什么x**4.0比Python 3 *中的 x**4

Python 3 int對象是一個完全成熟的對象,旨在支持任意大小; 由於這個事實,它們在C級別上處理 (看看所有變量如何在long_pow中聲明為PyLongObject *類型)。 這也使得它們的取冪變得更加棘手繁瑣,因為你需要使用它用來表示其值來執行它的ob_digit數組。 勇敢的來源。 - 請參閱: 了解更多關於PyLongObjectPython中大整數的內存分配 。)

相反,Python float對象可以轉換為C double類型(通過使用PyFloat_AsDouble ),並且可以使用這些本機類型執行操作。 這很好,因為在檢查相關的邊緣情況之后,它允許Python 使用平台的powC的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); 

其中iviw是我們原來的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 longint_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 ,這個太長了,無法在這里粘貼,所以這里是鏈接

它調用xnb_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM