Compiling the code and interfacing with Ctypes

Now let's compile the code we just wrote into a DLL or .so binary. This is actually fairly painless: if you are a Linux user, type the following into the command line to compile this file into mandelbrot.so:

nvcc -Xcompiler -fPIC -shared -o mandelbrot.so mandelbrot.cu

If you are a Windows user, type the following into the command line to compile the file into mandelbrot.dll

nvcc -shared -o mandelbrot.dll mandelbrot.cu

Now we can write our Python interface. We will start with the appropriate import statements, excluding PyCUDA completely and using just Ctypes. For ease of use, we'll just import all of the classes and functions from Ctypes directly into the default Python namespace, like so:

from __future__ import division
from time import time
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
from ctypes import *

Let's set up an interface for the launch_mandelbrot host-side function using Ctypes. First, we will have to load our compiled DLL or .so file as such (Linux users will, of course, have to change the file name to mandelbrot.so):

mandel_dll = CDLL('./mandelbrot.dll')
Now we can get a reference to launch_mandelbrot from the library, like so; we'll call it mandel_c for short:
mandel_c = mandel_dll.launch_mandelbrot

Now before we call a function with Ctypes, we will have to make Ctypes aware of what the input types are. Let's remember that for launch_mandelbrot, the inputs were float-pointer, float-pointer, integer, float, and integer. We set this up with the argtypes parameter, using the appropriate Ctypes datatypes (c_float, c_int), as well as the Ctypes POINTER class:

mandel_c.argtypes = [POINTER(c_float), POINTER(c_float), c_int, c_float, c_int]

Now let's write a Python function that will run this for us. We will specify the width and height of the square output image with breadth, and the minimum and maximum values in the complex lattice for both the real and imaginary components. We will also specify the maximum number of iterations, as well as the upper bound:

def mandelbrot(breadth, low, high, max_iters, upper_bound):

Now, we will create our lattice array with NumPy's linspace function, like so:

 lattice = np.linspace(low, high, breadth, dtype=np.float32)

Let's remember that we will have to pass a pre-allocated float array to launch_mandelbrot to get the output in the form of an output graph. We can do this by calling NumPy's empty command to set up an array of the appropriate shape and size, which will act as a C malloc call here:

    out = np.empty(shape=(lattice.size,lattice.size), dtype=np.float32)

Now, we are ready to compute the Mandelbrot graph. Notice that we can pass the NumPy arrays to C by using their ctypes.data_as method with the appropriate corresponding types. After we have done this, we can return the output; that is, the Mandelbrot graph in the form of a two-dimensional NumPy array:

 mandel_c(lattice.ctypes.data_as(POINTER(c_float)), out.ctypes.data_as(POINTER(c_float)), c_int(max_iters), c_float(upper_bound), c_int(lattice.size) ) 
return out

Now, let's write our main function to compute, time, and view the Mandelbrot graph with Matplotlib:

if __name__ == '__main__':
t1 = time()
mandel = mandelbrot(512,-2,2,256, 2)
t2 = time()
mandel_time = t2 - t1
print 'It took %s seconds to calculate the Mandelbrot graph.' % mandel_time
plt.figure(1)
plt.imshow(mandel, extent=(-2, 2, -2, 2))
plt.show()

We will now try running this. You should get an output that looks exactly like the Mandelbrot graph from Chapter 1Why GPU Programming? and Chapter 3, Getting Started with PyCUDA:

The code for this Python example is also available as the file mandelbrot_ctypes.py in the GitHub repository.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.188.121