8. How bifrost fits together¶
(aka seeing C in Python pipelines)
Bifrost is based on two maxims: 1) by itself, C sucks. 2) by itself, Python sucks.
Bifrost bridges the two languages using some clever python libraries, a customized numpy array class, and enforcing some pipeline friendly design choices.
8.1. A trip to the C¶
Suppose you have some custom C code which you want to integrate into a bifrost pipeline. Here’s a nice and simple snippet that adds two arrays together:
for(int i=0; i < nelements; i+=1)
{
x[i] = x[i] + y[i];
}
To get this into bifrost and use it in Python, you need to ‘bifrostify’
this code to accept bifrost’s special ndarray
class.
8.1.1. Bifrost Python ndarrays¶
Bifrost has a special array class in python that plays nicely with C:
import bifrost as bf
a = bf.ndarray([1,2,3,4,5,6,7,8,9,10], dtype='f32')
b = bf.ndarray([2,3,4,5,6,7,8,9,10,11], dtype='f32')
This ndarray
object is very similar to the numpy.array
, but it
has a special method, .as_BFarray()
. Essentially, as_BFarray
returns a pointer to the numpy array’s memory address, along with some
other useful stuff.
z = a.as_BFarray()
# Tab complete will show you this object has:
z.big_endian z.dtype z.shape
z.conjugated z.immutable z.space
z.data z.ndim z.strides
8.1.2. Bifrost C++ BFarray¶
In the bifrost C++ code, there is a matching BFarray
that makes
interfacing with Python straightforward. This is essentially a struct
that provides the same info as the Python ndarray
.
A usage example is worth a thousand words, so here is our simple snippet from before, after it’s been bifrostified:
BFstatus AddStuff(BFarray *xdata, BFarray *ydata)
{
long nelements = num_contiguous_elements(xdata);
float* x = (float *)xdata->data;
float* y = (float *)ydata->data;
for(int i=0; i < nelements; i +=1)
{
x[i] = x[i] + y[i];
}
return BF_STATUS_SUCCESS;
}
A full code example (with headers etc) can be found at the end.
8.1.3. Using your C++ code in Python¶
Now comes the cool part. You’ll need to edit three files:
create
add_stuff.cpp
insrc
create
add_stuff.h
insrc/bifrost
edit
src/Makefile
and addadd_stuff.o
in the LIBBIFROST_OBJS part
Now, run make
and make install
from the root directory to
rebuild the libbifrost.so
library.
Once this is recompiled, open up iPython and try this:
import bifrost as bf
from bifrost.libbifrost import _bf
b = bf.ndarray([1,2,3,4,5,6,7,8,9,10], dtype='f32')
a = bf.ndarray([1,2,3,4,5,6,7,8,9,10], dtype='f32')
_bf.AddStuff(a.as_BFarray(), b.as_BFarray())
print a
That is: your AddStuff
function is available in python via some
ctypesgen magic. You
can’t just pass a
and b
by themselves, but you can send their
as_BFarray()
output.
8.1.4. Wrapping up¶
Bravo, you’ve managed to run C++ code in bifrost! All the rest of the pipeline stuff is just python (to be continued…)
8.2. Example C++ codes¶
8.2.1. add_stuff.cpp¶
#include <bifrost/cpu_add.h>
#include <bifrost/array.h>
#include <bifrost/common.h>
#include <bifrost/ring.h>
#include <utils.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
extern "C" {
BFstatus AddStuff(BFarray *xdata, BFarray *ydata)
{
long nelements = num_contiguous_elements(xdata);
float* x = (float *)xdata->data;
float* y = (float *)ydata->data;
for(int i=0; i < nelements; i +=1)
{
x[i] = x[i] + y[i];
}
return BF_STATUS_SUCCESS;
}
}
8.2.2. bifrost/add_stuff.h¶
#include <bifrost/common.h>
#include <bifrost/array.h>
extern "C" {
BFstatus AddStuff(BFarray *xdata, BFarray *ydata);
}