Skip to content
keveman edited this page Nov 14, 2010 · 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 2 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

if_(expression) [
sequence of statements
].else_ [
sequence of statements
]
```

Clone this wiki locally