Exercise 11.2

Objectives:

  • An example of calling a simple C function from Python.

  • Use of the ctypes module to access C code without having to use a C compiler.

Files Created: Ext/cmandel.py

Files Modified: Ext/mandel.py

In the practical-python/Ext directory, you will find a pure-Python script mandel.py that creates a PNG file with a plot of the famous Mandelbrot set. Open that program with IDLE and run it now. You should get output such as this after about 15-30 seconds:

Wrote mandel.png
16.5698359013 seconds
>>>

As output, the program should create a file practical-python/Ext/mandel.png. If you go into the Ext/ directory and double-click on the file, you should see an image such as this:

mandel.png

(a) The Performance Problem

The script that generates the resulting image is entirely written in Python. However, almost all of the execution time is consumed by the following function (in mandel.py)

# Test a given x,y coordinate to see if it's a member of the set
def in_mandelbrot(x0,y0,n):
    x = 0
    y = 0
    while n > 0:
        xtemp = x*x - y*y + x0
        y = 2*x*y + y0
        x = xtemp
        n -= 1
        if x*x + y*y > 4: return False
    return True

Try running the mandel.py program under the Python profiler.

% python -m cProfile mandel.py

Look at the output and look at the entry for the in_mandelbrot() function.

(b) Using C Functions

The file Ext/src/mandel.c contains a C implementation of the in_mandelbrot() function. Here is what the code looks like this:

/* mandel.c */
/* Test a given x,y coordinate to see if it's a member of the set */
int
in_mandelbrot(double x0, double y0, int n) {
  double x=0,y=0,xtemp;
  while (n > 0) {
    xtemp = x*x - y*y + x0;
    y = 2*x*y + y0;
    x = xtemp;
    n -= 1;
    if (x*x + y*y > 4) return 0;
  }
  return 1;
}

If you take the above C function and compile it into a shared library, the ctypes library makes it easy to access. The following shared libraries have already been precompiled with the above function:

Ext/win32/libmandel.dll          (Windows)
Ext/osx/libmandel.so             (Macintosh OS-X)
Ext/linux/libmandel.so           (Linux)

Try using ctypes to load the appropriate library depending on what operating system you’re using:

>>> import ctypes
>>> ext = ctypes.cdll.LoadLibrary("./win32/libmandel.dll")
>>>

Now, get a reference to the in_mandelbrot() function and patch up its argument signatures:

>>> in_mandelbrot = ext.in_mandelbrot
>>> in_mandelbrot.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
>>> in_mandelbrot.restype = ctypes.c_int
>>>

Now, try calling the function for a few different values

>>> in_mandelbrot(0,0,500)
1
>>> in_mandelbrot(0.25,0.5,500)
1
>>> in_mandelbrot(2,2,500)
0
>>>

(c) A Performance Test

Take the mandel.py program and copy it to a new program cmandel.py. Modify the program so that the in_mandelbrot() function is loaded from a C library and used instead of the Python implementation.

Run your new program and compare its performance to the pure-Python implementation. Make sure it produces the correct output.

More Information

Instead of using ctypes, Python also provides a low-level C programming API that allows extension modules to be directly integrated with this interpreter. More information can be found in the file practical-python/Optional/Extension.pdf.

Credits:

The PNG output created in this example is being produced by the PyPNG package (http://code.google.com/p/pypng).

Links