Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python and performance of list comprehensions

Suppose you have got a list comprehension in python, like

Values = [ f(x) for x in range( 0, 1000 ) ]

with f being just a function without side effects. So all the entries can be computed independently.

Is Python able to increase the performance of this list comprehension compared with the "obvious" implementation; e.g. by shared-memory-parallelization on multicore CPUs?

like image 862
shuhalo Avatar asked Jun 02 '11 11:06

shuhalo


People also ask

Are Python list comprehensions faster?

Time Saved with List Comprehension. Because of differences in how Python implements for loops and list comprehension, list comprehensions are almost always faster than for loops when performing operations.

Why are list comprehensions faster Python?

List comprehensions are faster than for loops to create lists. But, this is because we are creating a list by appending new elements to it at each iteration.

How much faster are list comprehensions?

The list comprehension is 50% faster.

Are list comprehensions good?

List comprehensions are useful and can help you write elegant code that's easy to read and debug, but they're not the right choice for all circumstances. They might make your code run more slowly or use more memory.


2 Answers

In Python 3.2 they added concurrent.futures, a nice library for solving problems concurrently. Consider this example:

import math, time
from concurrent import futures

PRIMES = [112272535095293, 112582705942171, 112272535095293, 115280095190773, 115797848077099, 1099726899285419, 112272535095293, 112582705942171, 112272535095293, 115280095190773, 115797848077099, 1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def bench(f):
    start = time.time()
    f()
    elapsed = time.time() - start
    print("Completed in {} seconds".format(elapsed))

def concurrent():
    with futures.ProcessPoolExecutor() as executor:
        values = list(executor.map(is_prime, PRIMES))

def listcomp():
    values = [is_prime(x) for x in PRIMES]

Results on my quad core:

>>> bench(listcomp)
Completed in 14.463825941085815 seconds
>>> bench(concurrent)
Completed in 3.818351984024048 seconds
like image 57
zeekay Avatar answered Sep 26 '22 10:09

zeekay


No, Python will not magically parallelize this for you. In fact, it can't, since it cannot prove the independence of the entries; that would require a great deal of program inspection/verification, which is impossible to get right in the general case.

If you want quick coarse-grained multicore parallelism, I recommend joblib instead:

from joblib import delayed, Parallel
values = Parallel(n_jobs=NUM_CPUS)(delayed(f)(x) for x in range(1000))

Not only have I witnessed near-linear speedups using this library, it also has the great feature of signals such as the one from Ctrl-C onto its worker processes, which cannot be said of all multiprocess libraries.

Note that joblib doesn't really support shared-memory parallelism: it spawns worker processes, not threads, so it incurs some communication overhead from sending data to workers and results back to the master process.

like image 36
Fred Foo Avatar answered Sep 23 '22 10:09

Fred Foo