[python] Python 3에서 x ** 4.0이 x ** 4보다 빠른 이유는 무엇입니까?

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


TigerhawkT3 은 루프 외부에서는 발생하지 않는다고 지적했습니다. 나는 확인하고 상황은 (내가 본 것에서) 베이스 가 올라갈 때만 발생합니다 . 그것에 대한 아이디어가 있습니까?



답변

x**4.0 빨리 보다는 x**4파이썬 3 * ?

파이썬 3 int객체는 임의의 크기를 지원하도록 설계된 본격적인 객체입니다. 그 사실 때문에 C 수준에서 그대로 처리됩니다 (모든 변수가 PyLongObject *유형으로 선언되는 방법 참조 long_pow). 이것은 또한 지수 훨씬 더하게 까다 하고 지루한을 당신이 함께 놀러 할 필요가 있기 때문에 ob_digit그것을 수행하기 위해 그 값을 표현하기 위해 사용하는 배열입니다. ( 출처 용감한합니다. – 참조 : 파이썬에서 큰 정수에 대한 이해 메모리 할당 에 대한 자세한 내용은 PyLongObject.들)

float반대로 Python 객체는 (를 사용하여 ) C 유형 으로 변환 할 수 있으며 해당 기본 유형을 사용하여 작업을 수행 할 수 있습니다 . 이것은 대단한 관련 에지 경우에 확인 후, 그것은 파이썬을 허용하기 때문에 플랫폼 ‘를 사용 ( C의 입니다 ) 실제 지수를 처리하는 :doublePyFloat_AsDoublepowpow

/* 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우리의 원래있는 PyFloatObjectC 등의 double의.

그만한 가치가있는 것은 : Python 2.7.13for me는 2~3더 빠르며 반대 행동을 보여줍니다.

이전의 사실 은 또한 파이썬 2와 3의 불일치를 설명 하므로 흥미 롭기 때문에이 의견도 다루겠다고 생각했습니다.

Python 2에서는 Python 3 intint객체 와 다른 오래된 객체를 사용하고 있습니다 ( 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'>당신은, 랩 된 경우의 xA의 이름 long(기본적으로 사용하도록 강제 파이썬 2에서 전화를 long_pow파이썬 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

메모를 받아, 그 한 조각을 변환하는 비록 intlong(@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

동일한 바이트 코드는 int 대신 float 를 로드 '4 ** 4.'한다는 점만 다릅니다 .LOAD_CONST256.0256

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_Powerternary_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);
    ...


답변

하나는 정확하기 때문에 다른 것은 근사치입니다.

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


답변