2013-05-19 10 views
14

Jeśli spojrzeć na następujące czasy:int .__ mul__, wykonuje 2x wolniej niż operator.mul

C:\Users\Henry>python -m timeit -s "mul = int.__mul__" "reduce(mul,range(10000))" 
1000 loops, best of 3: 908 usec per loop 

C:\Users\Henry>python -m timeit -s "from operator import mul" "reduce(mul,range(10000))" 
1000 loops, best of 3: 410 usec per loop 

Istnieje znaczna różnica w szybkości wykonania między

reduce(int.__mul__,range(10000)) i reduce(mul,range(10000)) z czym te ostatnie są szybsze .

użyciu modułu dis spojrzeć na to, co się dzieje:

Korzystanie int.__mul__ metody:

C:\Users\Henry>python 
Python 2.7.4 (default, Apr 6 2013, 19:55:15) [MSC v.1500 64 bit (AMD64)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> mul = int.__mul__ 
>>> def test(): 
...  mul(1,2) 
... 
>>> import dis 
>>> dis.dis(test) 
    2   0 LOAD_GLOBAL    0 (mul) 
       3 LOAD_CONST    1 (1) 
       6 LOAD_CONST    2 (2) 
       9 CALL_FUNCTION   2 
      12 POP_TOP 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 
>>> 

i operator mul metodę

C:\Users\Henry>python 
Python 2.7.4 (default, Apr 6 2013, 19:55:15) [MSC v.1500 64 bit (AMD64)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from operator import mul 
>>> def test(): 
...  mul(1,2) 
... 
>>> import dis 
>>> dis.dis(test) 
    2   0 LOAD_GLOBAL    0 (mul) 
       3 LOAD_CONST    1 (1) 
       6 LOAD_CONST    2 (2) 
       9 CALL_FUNCTION   2 
      12 POP_TOP 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE 
>>> 

one wyglądają tak samo, więc dlaczego jest istnieje różnica w szybkości realizacji? Mam na myśli realizację CPython Pythona


To samo dzieje się na python3:

$ python3 -m timeit -s 'mul=int.__mul__;from functools import reduce' 'reduce(mul, range(10000))' 
1000 loops, best of 3: 1.18 msec per loop 
$ python3 -m timeit -s 'from operator import mul;from functools import reduce' 'reduce(mul, range(10000))' 
1000 loops, best of 3: 643 usec per loop 
$ python3 -m timeit -s 'mul=lambda x,y:x*y;from functools import reduce' 'reduce(mul, range(10000))' 
1000 loops, best of 3: 1.26 msec per loop 
+5

Patrzysz na demontaż kodu bajtowego 'test()' i po prostu wywołuje 'mul', a więc jest taki sam w obu przypadkach. To dwie implementacje 'mul', które prawdopodobnie różnią się. –

+0

@HristoIliev Dzięki, nie powiedziałem, że to był tylko test demontażu. Przypuszczam, że ma to dużo więcej sensu. Pójdę sprawdzić, jak te zostały wdrożone więcej niż wtedy. – HennyH

+0

Czy używasz Pythona dwa? Problem może polegać na tym, że mulę int przepełni się i wywoła długi mul podczas gdy operator uniknie dodatkowych połączeń. – Bakuriu

Odpowiedz

14

int.__mul__ jest opakowaniem gniazdo, a mianowicie PyWrapperDescrObject, natomiast operator.mul jest funkcją buit-in. Myślę, że przeciwna szybkość wykonania jest spowodowana tą różnicą.

>>> int.__mul__ 
<slot wrapper '__mul__' of 'int' objects> 
>>> operator.mul 
<built-in function mul> 

Kiedy nazywamy PyWrapperDescrObject, wrapperdescr_call nazywa.


static PyObject * 
wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) 
{ 
    Py_ssize_t argc; 
    PyObject *self, *func, *result; 

    /* Make sure that the first argument is acceptable as 'self' */ 
    assert(PyTuple_Check(args)); 
    argc = PyTuple_GET_SIZE(args); 
    if (argc d_type->tp_name); 
     return NULL; 
    } 
    self = PyTuple_GET_ITEM(args, 0); 
    if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), 
            (PyObject *)(descr->d_type))) { 
     PyErr_Format(PyExc_TypeError, 
        "descriptor '%.200s' " 
        "requires a '%.100s' object " 
        "but received a '%.100s'", 
        descr_name((PyDescrObject *)descr), 
        descr->d_type->tp_name, 
        self->ob_type->tp_name); 
     return NULL; 
    } 

    func = PyWrapper_New((PyObject *)descr, self); 
    if (func == NULL) 
     return NULL; 
    args = PyTuple_GetSlice(args, 1, argc); 
    if (args == NULL) { 
     Py_DECREF(func); 
     return NULL; 
    } 
    result = PyEval_CallObjectWithKeywords(func, args, kwds); 
    Py_DECREF(args); 
    Py_DECREF(func); 
    return result; 
} 

Spójrzmy na to, co znaleźliśmy!

func = PyWrapper_New((PyObject *)descr, self); 

Nowy obiekt PyWrapper został skonstruowany. Znacznie spowolniłoby to szybkość wykonania. Czasami utworzenie nowego obiektu zajmuje więcej czasu niż uruchomienie prostej funkcji.
Dlatego nie jest zaskoczeniem, że int.__mul__ jest wolniejsza niż operator.mul.

Powiązane problemy