Feb 23, 2013

Speeding up Python performance with Cython (II)

Following up on the last article and as you can see in the previous script, first of all the full path of the Cython installation has been added to the PYTHONPATH. By running this script, both a C file (calculate_primes1.c) and a loadable module by Python (calculate_primes1.so) are been generated.

cython$ python setup.py build_ext --inplace
running build_ext
cythoning calculate_primes1.pyx to calculate_primes1.c
building 'calculate_primes1' extension
gcc -pthread -fno-strict-aliasing -DNDEBUG -fmessage-length=0 -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g -fwrapv -fPIC -I/usr/include/python2.6 -c calculate_primes1.c -o build/temp.linux-x86_64-2.6/calculate_primes1.o
gcc -pthread -shared build/temp.linux-x86_64-2.6/calculate_primes1.o -L/usr/lib64 -lpython2.6 -o calculate_primes1.so

cython$ ls -l
drwxr-xr-x  3 javi javi  4096 Feb 16 17:46 build
-rw-r--r--  1 javi javi 68295 Feb 17 11:27 calculate_primes1.c
-rw-r--r--  1 javi javi   393 Feb 17 11:06 calculate_primes1.pyx
-rwxr-xr-x  1 javi javi 53388 Feb 17 11:27 calculate_primes1.so
-rwxr-xr-x  1 javi javi   393 Feb 16 21:43 calculate_primes.py
-rwxr-xr-x  1 javi javi   530 Feb 16 21:49 calculate_primes.pyc
drwxr-xr-x 10 javi javi  4096 Feb 16 16:34 Cython-0.18
-rwxr-xr-x  1 javi javi   314 Feb 17 11:06 setup.py
-rwxr-xr-x  1 javi javi   426 Feb 16 21:53 test.py


If you take a look at the C file, you will appreciate that is a huge file (compared with the Python version) where some parts of the code have been translated to C and others keep as Python API calls.

A handy tool aimed at turning out an HTML report which shows the Cython code interlined with the C code, is the own compiler executed with the "-a" option.

cython$ Cython-0.18/cython.py -a calculate_primes1.pyx


If you open the come out HTML file, you will see that lines are colored according to the level of "typedness" (white lines translates to pure C without any Python API calls).



Now let's run again this new version of the module compiled with Cython (remember to change the name of the module inside the test.py file). As you can derive from the result, the performance has been enhanced about 30%.

cython$ python test.py 
1.20011019707


Let's go a step further and add some static types to a second version of our module (called now calculate_primes2).

def calculate_primes(int limit):
    primes = []
    cdef int number = 0
    cdef int divisor = 0

    for number in range(limit):
        for divisor in range(2, number+1):
            if number % divisor == 0 and number == divisor:
                primes.append(number)
                break
            elif number % divisor == 0 and number != divisor:
                break

    return primes


No comments:

Post a Comment