Why is manual string inversion worse than reverse in Python 2.7? What is the algorithm used in Slice?

Below is the performance difference between Slice and manual reverse mode. If so, what is the reason for this?

timeit.timeit("a[::-1]","a=[1,2,3,4,5,6]",number=100) 6.054327968740836e-05 timeit.timeit("[a[i] for i in range(len(a)-1,-1,-1)]","a=[1,2,3,4,5,6]",number=100) 0.0003132152330920235 
+5
source share
3 answers

Here is the bytecode

 from dis import dis a = [1,2,3,4,5,6] def func1(): a[::-1] def func2(): [a[i] for i in range(len(a)-1,-1,-1)] def func3(): reversed(a) 

In the second method, you find the length, create a copy with a range, and create the variable i.

bytecode

You can also use the opposite to create an iteration.

bytecode2

+8
source

The designation of the fragment for accessing the list drops in C, which is much faster than the implementation on the python chip in the reverse order. For example, in a pure python approach, the python interpreter must read, decode, and execute each instruction in byte code, while a C call will be executed initially and will not have such a penalty. This punishment also extends to things like finding methods when indexing an element, etc. While there is no method in the C call, just address the arithmetic. The C implementation is so effective that it doesn’t even bother with the specialized back-cut function and still surpasses the pure python implementation. Rather, it creates a copy of the slice and reverses the slice in place (made elsewhere).

List of snippets for cpython :

 static PyObject * list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) { PyListObject *np; PyObject **src, **dest; Py_ssize_t i, len; if (ilow < 0) ilow = 0; else if (ilow > Py_SIZE(a)) ilow = Py_SIZE(a); if (ihigh < ilow) ihigh = ilow; else if (ihigh > Py_SIZE(a)) ihigh = Py_SIZE(a); len = ihigh - ilow; np = (PyListObject *) PyList_New(len); if (np == NULL) return NULL; src = a->ob_item + ilow; dest = np->ob_item; for (i = 0; i < len; i++) { PyObject *v = src[i]; Py_INCREF(v); dest[i] = v; } return (PyObject *)np; } 
+2
source

Showdowns for 3 different versions - (no screenshots):

 import dis a = [1,2,3,4,5,6] def x( l ): return l[::-1] dis.dis(x) 2 0 LOAD_FAST 0 (l) 3 LOAD_CONST 0 (None) 6 LOAD_CONST 0 (None) 9 LOAD_CONST 1 (-1) 12 BUILD_SLICE 3 15 BINARY_SUBSCR 16 RETURN_VALUE def y( l ): return [l[i] for i in range(len(l)-1,-1,-1)] dis.dis(y) 2 0 BUILD_LIST 0 3 LOAD_GLOBAL 0 (range) 6 LOAD_GLOBAL 1 (len) 9 LOAD_FAST 0 (l) 12 CALL_FUNCTION 1 15 LOAD_CONST 1 (1) 18 BINARY_SUBTRACT 19 LOAD_CONST 2 (-1) 22 LOAD_CONST 2 (-1) 25 CALL_FUNCTION 3 28 GET_ITER >> 29 FOR_ITER 16 (to 48) 32 STORE_FAST 1 (i) 35 LOAD_FAST 0 (l) 38 LOAD_FAST 1 (i) 41 BINARY_SUBSCR 42 LIST_APPEND 2 45 JUMP_ABSOLUTE 29 >> 48 RETURN_VALUE def z( l ): return [i for i in reversed(a)] dis.dis(z) 2 0 BUILD_LIST 0 3 LOAD_GLOBAL 0 (reversed) 6 LOAD_GLOBAL 1 (a) 9 CALL_FUNCTION 1 12 GET_ITER >> 13 FOR_ITER 12 (to 28) 16 STORE_FAST 1 (i) 19 LOAD_FAST 1 (i) 22 LIST_APPEND 2 25 JUMP_ABSOLUTE 13 >> 28 RETURN_VALUE 
0
source

Source: https://habr.com/ru/post/1200089/


All Articles