Reputation: 525
Below the performance difference between Slice and manual reverse operation. If this is the case, What is the reason for that?
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
Upvotes: 5
Views: 648
Reputation: 40683
The slice notation for reversing a list drops down into C, which is considerably faster than a pure python implementation of a reverse. For instance, in the pure python approach the python interpreter must read, decode, and execute each instruction in the byte code, whereas the C call will be executing natively and suffer no such penalty. This penalty also extends to things such as method lookups when indexing an item and so forth, whereas in the C call there is no method, just address arithmetic. So efficient is the C implementation that it doesn't even bother with a specialised reversed slice function, and still beats the pure python implementation. Rather it creates a copy of the slice and the reverses the slice in place (done else where).
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;
}
Upvotes: 2
Reputation: 9704
The Dissasembly for 3 different versions - (without screen shots) :
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
Upvotes: 0
Reputation: 1380
Here's 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're finding the length, creating a copy with range and creating the variable i.
Can also use reversed to create an iterable.
Upvotes: 8