2013-05-19 31 views
14

Nếu bạn nhìn vào các timings sau:int .__ mul__, thực hiện chậm hơn so với 2X 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 

Có sự khác biệt đáng kể về tốc độ thực thi giữa

reduce(int.__mul__,range(10000))reduce(mul,range(10000)) với sau này là nhanh hơn .

sử dụng các mô-đun dis để nhìn vào những gì đã xảy ra:

Sử dụng int.__mul__ phương pháp:

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 
>>> 

Và các nhà điều hành mul phương pháp

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 
>>> 

Họ xuất hiện như nhau, vậy tại sao có một sự khác biệt trong tốc độ thực hiện? Tôi đề cập đến việc thực hiện CPython của Python


Cùng xảy ra trên 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

Bạn đang xem xét việc tháo gỡ bytecode của 'test()' và nó chỉ gọi là 'mul', do đó nó giống nhau trong cả hai trường hợp. Đó là hai cách thực hiện của 'mul' có lẽ khác nhau. –

+0

@HristoIliev Cảm ơn, tôi đã không chỉ là thử nghiệm tháo rời. Tôi cho rằng điều đó có ý nghĩa hơn rất nhiều. Tôi sẽ xem xét cách chúng được thực hiện một số chi tiết sau đó. – HennyH

+0

Bạn đang sử dụng python hai? Vấn đề có thể là mul của int sẽ tràn và gọi dài của mul trong khi nhà điều hành tránh các cuộc gọi thêm này. – Bakuriu

Trả lời

14

int.__mul__ là một wrapper khe, cụ thể là, một PyWrapperDescrObject, trong khi operator.mul là một chức năng buit-in. Tôi nghĩ tốc độ thực hiện ngược lại là do sự khác biệt này.

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

Khi chúng tôi gọi số PyWrapperDescrObject, wrapperdescr_call được gọi.


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; 
} 

Hãy xem những gì chúng tôi đã tìm thấy!

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

Một đối tượng PyWrapper mới đã được tạo. Nó sẽ làm chậm tốc độ thực thi đáng kể. Đôi khi, phải mất nhiều thời gian hơn để tạo một đối tượng mới hơn là chạy một hàm đơn giản.
Do đó, không ngạc nhiên khi int.__mul__ chậm hơn operator.mul.

Các vấn đề liên quan