I have tried to understand Clyther. I have failed because the code is quite
complex. But I have some understanding of the AST, so I was able to implement
my own python -> C compiler which makes OpenCL friendly code and works with
pyOpenCL.
The idea is that of writing kernels in Python, not C, and adding a decorator to
compile them to C99.
The code can be found at https://github.com/mdipierro/mdpcl and it is very
short (all in one file, device.py and one example file).
It requires pyopencl, https://github.com/srossross/Meta, matplotlib and
https://github.com/mdipierro/canvas
Here for example is a 2D laplace solver in pure python (with pyopencl of course)
from device import Device
from canvas import Canvas
import random
import numpy
n = 300
q = numpy.zeros((n,n), dtype=numpy.float32)
u = numpy.zeros((n,n), dtype=numpy.float32)
w = numpy.zeros((n,n), dtype=numpy.float32)
for k in range(n):
q[random.randint(1,n-1),random.randint(1,n-1)] = random.choice((-1,+1))
device = Device()
q_buffer = device.buffer(source=q, mode=device.flags.READ_ONLY)
u_buffer = device.buffer(source=u)
w_buffer = device.buffer(source=w)
@device.define('kernel',
w='global:ptr_float',
u='global:const:ptr_float',
q='global:const:ptr_float')
def solve(w,u,q):
x = new_int(get_global_id(0))
y = new_int(get_global_id(1))
n = new_int(get_global_size(0))
site = new_int(x*n+y)
if y!=0 and y!=n-1 and x!=0 and x!=n-1:
up = new_int(site-n)
down = new_int(site+n)
left = new_int(site-1)
right = new_int(site+1)
w[site] = 1.0/4*(u[up]+u[down]+u[left]+u[right] - q[site])
program = device.compile(device.define.code)
for k in range(3000):
program.solve(device.queue, [n,n], None, w_buffer, u_buffer, q_buffer)
(u_buffer, w_buffer) = (w_buffer, u_buffer)
u = device.retrieve(u_buffer,shape=(n,n))
Canvas(title='').imshow(u).save(filename='plot.png')
Notice the solve function. It is converted at runtime to the following C99 code:
"""
__kernel void solve(__global float* w, __global const float* u, __global const
float* q);
__kernel void solve(__global float* w, __global const float* u, __global const
float* q) {
int right;
int site;
int up;
int down;
int y;
int x;
int n;
int left;
x = get_global_id(0);
y = get_global_id(1);
n = get_global_size(0);
site = ((x * n) + y);
if(((y != 0) && ((y != (n - 1)) && ((x != 0) && (x != (n - 1)))))) {
up = (site - n);
down = (site + n);
left = (site - 1);
right = (site + 1);
w[site] = ((1.0 / 4) * ((((u[up] + u[down]) + u[left]) + u[right]) -
q[site]));
}
}
""""
If a function is decorated with @device.define(…) it means that all functions
called inside that function are defined on the opencl side. If the decorator
says kernel it adds the "__kernel" modifier. You can decorate more than one
function and they are all compiled together. Variables must be declared with
new_int, new_float, new_float2, etc.
It seems to work but I could use some feedback.
Massimo_______________________________________________
PyOpenCL mailing list
[email protected]
http://lists.tiker.net/listinfo/pyopencl