possibly trivial newbie list/array question

Skip Montanaro skip at pobox.com
Wed Aug 22 08:18:56 EDT 2001


    Paul> I just tried your example in Python 2.1.1, Linux, P3-750, lots of
    Paul> ram, but with a 100 element list instead of 3 elements.

    Paul>         a = map(lambda x: x+1.0, range(100))

    Paul> Results (running each method 10,000 times like in your example):

    Paul>         map(lambda x: 4.0*x, a)       1.97 seconds
    Paul>         [4.0*x for x in a]            3.57 seconds

    Paul> I don't know why the list comprehension is slower than the
    Paul> map/lambda for this larger list.

A peek at the bytecode might help.  Here's the bytecode for calling map:

     0 LOAD_GLOBAL         0 (map)
     3 LOAD_CONST          1 (<code object <lambda> at 0x8281890, file "<stdin>", line 2>)
     6 MAKE_FUNCTION       0
     9 LOAD_FAST           0 (a)
    12 CALL_FUNCTION       2

and the bytecode for the lambda:

     0 LOAD_CONST          1 (4.0)
     3 LOAD_FAST           0 (x)
     6 BINARY_MULTIPLY
     7 RETURN_VALUE   

Here's the list comprehension bytecode:

     0 BUILD_LIST          0
     3 DUP_TOP        
     4 LOAD_ATTR           0 (append)
     7 STORE_FAST          2 (_[1])
    10 LOAD_FAST           0 (a)
    13 LOAD_CONST          1 (0)
    16 FOR_LOOP           20 (to 39)
    19 STORE_FAST          1 (x)
    22 LOAD_FAST           2 (_[1])
    25 LOAD_CONST          2 (4.0)
    28 LOAD_FAST           1 (x)
    31 BINARY_MULTIPLY
    32 CALL_FUNCTION       1
    35 POP_TOP        
    36 JUMP_ABSOLUTE      16
    39 DELETE_FAST         2 (_[1])

I think the map/lambda version has a relatively high cost to crank up (make
the lambda, call map), while the list comprehension version has a lower
startup cost, but a higher per-iteration cost.

Also, note that the performance of the list comprehension version will be
hurt more than the map/lambda version if you ran your code at a module scope
(the dis output above was from code inside functions).  All the LOAD_FAST
and STORE_FAST instructions (both array indexing operations with no function
call) inside the for loop would become LOAD_GLOBAL and STORE_GLOBAL
instructions (both call PyDict_* functions).  The map/lambda case doesn't
suffer from this problem.  Using this code:

    import time

    a = map(lambda x: x+1.0, range(100))

    t = time.clock()
    for i in range(10000):
        x = map(lambda x: 4.0*x, a)
    print "map/lambda:", round(time.clock()-t, 2)

    t = time.clock()
    for i in range(10000):
        x = [4.0*x for x in a]
    print "list comprehension:", round(time.clock()-t, 2)

    def f(a):
        t = time.clock()
        for i in range(10000):
            x = map(lambda x: 4.0*x, a)
        print "map/lambda (func):", round(time.clock()-t, 2)

    f(a)

    def f(a):
        t = time.clock()
        for i in range(10000):
            x = [4.0*x for x in a]
        print "list comprehension (func):", round(time.clock()-t, 2)

    f(a)

I got this output:

    % python lcvsmap.py 
    map/lambda: 2.88
    list comprehension: 5.17
    map/lambda (func): 2.94
    list comprehension (func): 4.18

which suggests to me that you didn't put your tests in functions.  Alex may
have.  ;-)

bake-offs-always-need-recipes-ly y'rs,

-- 
Skip Montanaro (skip at pobox.com)
http://www.mojam.com/
http://www.musi-cal.com/




More information about the Python-list mailing list