Because I’ve been generating a lot of fractal images recently, I’ve found myself spending quite a bit of time sitting around waiting for programs I’ve written to finish long repetitive calculations. In particular, I’ve generated several long sequences of several hundred images that I subsequently combined into animations.

One of the many great things about teaching electrical engineering is that I get to dip in and out of different programming languages all the time, but C remains my primary language and it’s still what I’m most likely to reach for when I’m writing something for my own curiosity. However, over the last couple of months I’ve been using Python for most of my fractal programming because I find its complex number syntax elegant. Furthermore, although complex numbers have been natively supported in C since the C99 standard, I’ve just never really embraced that language feature (until now).

Anyway, because I’ve been spending all this time waiting while numbers are getting crunched, I decided to do a quick comparison of execution time for the same task between Python and C. Naturally, since C is compiled and Python is interpreted, it comes as no surprise that C is significantly faster. Nevertheless, I was still surprised by *how much* faster it is.

The task I chose for this experiment was the generation of a Mandelbrot Set image of resolution 4000×4000 pixels, spanning a square region of the complex plane from -2 to +2 on the real axis and from -2j to +2j on the imaginary axis. This is the image produced:

I implemented the same algorithm in C and Python and then performed a rough measurement of the execution time of each program by running the “date” command line tool immediately before and after it, as shown below…

As you can see, the approximate execution times were as follows:

- Python implementation: 105 seconds
- C implementation: 4 seconds

In other words, the C implementation executes more than 26 times faster than the Python implementation! Compiling the program with gcc (also shown in the above screen shot) only takes a fraction of a second too. I’m sure a better programmer could make either of these implementations run a bit faster, but unless the Python version can run *dramatically* faster than this then the difference is more than enough to persuade me to embrace complex maths in C.

This is the full C code:

// // mandelbrot.c - written by Ted Burke - 2-Feb-2016 // // To compile: // gcc mandelbrot.c -o mandelbrot -lm // #include <complex.h> #include <stdio.h> // Create an array of bytes to store pixel values unsigned char p[4000][4000]; void main() { double complex c, z; int w=4000, h=4000, x, y; unsigned char px; // Open output file FILE* f = fopen("mandelbrot_c.pgm","w"); fprintf(f, "P5\n%d %d\n255\n", w, h); // Calculate pixel values for (y=0 ; y<h ; ++y) { for (x=0 ; x<w ; ++x) { // Initialise complex values c and z for next // iterative pixel value calculation c = (-2.0 + x*4.0/w) + (2.0 - y*4.0/h)*I; z = 0; // Iterate complex function while darkening pixel px = 255; while(px >= 10 && cabs(z) < 4.0) { z = z*z + c; px -= 10; } // Store latest pixel value in byte array p[y][x] = px; } } // Write the byte array to the output file fwrite(p, 1, w*h, f); fclose(f); }

This is the full Python code:

# # mandelbrot.py - written by Ted Burke - 2-Feb-2016 # import numpy # Create an array of bytes to store pixel values w,h = 4000,4000 p = numpy.zeros((h,w),dtype=numpy.uint8) # Open output file f = file('mandelbrot_python.pgm','w') f.write('P5\n{:d} {:d}\n255\n'.format(w, h)) # Calculate pixel values for y in range(h): for x in range(w): # Initialise complex values c and z for next # iterative pixel value calculation c = (-2.0 + x*4.0/w) + (2.0 - y*4.0/h)*(0+1j) z = 0 + 0j # Iterate complex function while darkening pixel px = 255 while px >= 10 and abs(z) < 4.0: z = z*z + c px -= 10 # Store latest pixel value in byte array p[y][x] = px # Write the byte array to the output file f.write(bytearray(p)) f.close()