5

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
Korem
  • 11,383
  • 7
  • 55
  • 72
logeekal
  • 525
  • 4
  • 13
  • 3
    It would be interesting to see the opcodes, but remember that the 2nd example creates a 2nd list (as a result of the range call), where as the first one doesn't (well not a python range) - so that might be one of the reasons. – Tony Suffolk 66 Aug 12 '14 at 07:57
  • Both versions involve loops, but with the slice the loop is performed by the python interpreter (written in C) and thus much faster than the python loop. – georg Aug 12 '14 at 08:48

3 Answers3

8

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.

bytecode

Can also use reversed to create an iterable.

bytecode2

flau
  • 1,328
  • 1
  • 20
  • 36
2

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

List slice code 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;
}
Dunes
  • 37,291
  • 7
  • 81
  • 97
0

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    
Tony Suffolk 66
  • 9,358
  • 3
  • 30
  • 33