Skip to content
keveman edited this page Apr 19, 2011 · 36 revisions

Introduction

Carbon is a simple language embedded in C++. The syntax is a subset of Boost.Phoenix. Carbon has been implemented with Thrust in mind. Therefore its main purpose is to act as a lambda library for Thrust. Carbon makes it more convenient to specify simple functors as arguments to Thrust functions like thrust::transform and thrust::reduce. Here is an example :

SAXPY in Thrust without Carbon
struct saxpy_functor : public thrust::binary_function<float,float,float>
{
    const float a;

    saxpy_functor(float _a) : a(_a) {}

    __host__ __device__
        float operator()(const float& x, const float& y) const { 
            return a * x + y;
        }
};

void saxpy_fast(float A, thrust::device_vector<float>& X, thrust::device_vector<float>& Y)
{
    // Y <- A * X + Y
    thrust::transform(X.begin(), X.end(), Y.begin(), Y.begin(), saxpy_functor(A));
}
SAXPY in Thrust with Carbon
using namespace carbon::lambda;
void saxpy_fast(float A, thrust::device_vector<float>& X, thrust::device_vector<float>& Y)
{
    // Y <- A * X + Y
    thrust::transform(X.begin(), X.end(), Y.begin(), Y.begin(), A*_1+_2);
}

Here is a somewhat complex example of a program that you can write in Carbon.

int i=0, j=41;

let_(_a=_2) [
  while_(_a >= 0) [
    _1 = _1 + 1,
    _a = _a-1
  ]
] (i, j);

cout << i << "\n";

The above code should print
42

Building Function Objects with Carbon

Carbon defines a set of predefined symbols (like _1, _2, _a, _b, etc) and overloads most operators for these symbols. You can form C++ expressions using these symbols and operators. Let’s call these expressions Carbon programs. For example,

_1+_2

and
_2 = _1 + 42

are Carbon programs. Carbon programs have the function call operator (operator()) overloaded. Therefore you can use Carbon programs as regular C++ functions, or pass them around as function objects. (As of this writing, Carbon programs can take only 4 parameters). In Thrust context, you can pass a Carbon program as an argument to, say, thrust::transform. That Carbon program gets pass down to the GPU, and executes on the device! There are 2 kinds of Carbon programs, as described below.

Functional Carbon Programs

These are programs that look like C++ r-values, i.e., they don’t have assignments. In other words, they are pure functions. For example,

_1 + _2

is a functional Carbon program, and calling it like this
(_1+_2)(1, 41)

returns 42.

Imperative Carbon Programs

These are programs that have assignment statements, sequence of statements, if-then-else constructs, etc. They return void, and to get back result from them, you have to pass an l-value as one of the arguments to the call. For example,

int i;
(_1 = _2+1)(i, 41)
cout << i << "\n";

prints out 42.

Components of Carbon

Argument Placeholders

Carbon defines the constant objects _1, _2, _3, _4 in the carbon::lambda namespace. They act as placeholders or proxies for parameters passed when calling a Carbon programs. _1 is a placeholder for the first parameter, _2 for the second parameter and so on.

Operators

Carbon overloads most operators for the Carbon expressions. Therefore you can form expressions using operators like +,-,*,/. (As of this writing, the pre/post increment operators ++, -- and the index operator [] have not been overloaded.)

Assignment Statement

Carbon overloads the assignment operator =, so you can write assignments of the form _1=_2+41. Note that you can only use the simple assignment operator = in assignment statements. Other assignment operators like +=, -=, |= etc. are not allowed.

Sequence of Statements

You can form a sequence of statements using the comma (,) operator. Example :

_1 = _2, _2 = 3.14*_1*_1

You can think of the comma operator as the semi-colon (;) of Carbon programs.

if-then-else Statement

You can perform conditional execution in Carbon using if-then-else statements as follows :

if_(expression) [
  /* sequence of statements */
].else_ [
  /* sequence of statements */
]

Note that the sequence can themselves be assignments, or even other if-then-else statements. Note the use of [] operator. You can think of the [] operator as the braces ({}) of Carbon programs.

while Statement
while_(expression) [
  /* sequence of statements */
]

Local Variables and let_ Block Statement

It’s hard to write programs using only 4 variables (_1,_2,_3,_4). Therefore, Carbon defines local variables _a, _b, _c, ..., _z. You can freely use these local variables in Carbon programs. However, you have to define them first and scope the statements where the local variable is visible. This is done using the let_ statement block as follows :

let_(_a=expr) [
  /* sequence of statements */
]

As of this writing, you can only define one local variable with the let_ construct. However, you can define as many local variables with nested @let_@s as follows :
let_(_a=expr1) [
  let_(_b=expr2) [
    /* sequence of statements */
  ]
] 

Thrust specific Functions

  • get_<N>(expr) – Treats expr as a tuple and calls thrust::get<N> on it.
  • max_, min_