This example introduces task launches and futures in Regent with a naive (but parallel) implementation of the Fibonacci numbers. This is not the fastest way to compute Fibonacci numbers, but demonstrates the functional nature of Regent tasks. The complete code for this example follows at the bottom of the page and can also be found in the GitHub repository.
Tasks are the fundamental unit of execution in Regent. Tasks are like functions in traditional languages. In fact, tasks execute sequentially. This means you can read the body of task from top-to-bottom, just like a normal sequential language.
Parallelism occurs only among child tasks. (The terms child tasks
and subtasks simply refer to the set of tasks called by a given
task.) Though the two calls to
fibonacci below appear to execute in
sequence, they will actually run in parallel with each other.
Two tasks can execute in parallel only if they do not interfere. For the two calls above, this is trivial: the parameters to the tasks above are passed by-value, and there are no mutable global variables in Regent. Thus there is no way for them to modify state used in the other task (and thus no possibility of interference).
Regent does support mutable state, however. We’ll consider this (and how it results in potential interference) in later tutorials.
fibonacci task itself is declared to take two
and returns an
int. (The return type can be inferred in general and
is shown below for pedagogical purposes only.) The body of the task
stores the results of the two recursive calls in variables
f2 and returns the sum of their values.
In order to maximize parallelism, it’s important to avoid blocking the
execution of a task as it calls subtasks. Regent has a number of
optimizations that help ensure this. For example, the
above returns a future, rather than a direct value. This means that
the execution continues past the line
var f1 = fibonacci(n - 1) to
fibonacci call, even though the result of the call may
not be ready yet. The
+ operator is also lifted to operate on
futures. Thus, the task only blocks at the final
after all parallel operations have been issued.
It is important to note that futures are not an a part of the Regent programming model. They are purely an optimization, inserted automatically by the compiler where appropriate. However, there are a number of ways to defeat the optimization—for example, a call to a C function cannot be issued in parallel, and thus must block execution if one of the arguments is a future.
The main task calls
fibonacci in a loop. In order to avoid blocking
on the call to
c.printf, the call is extracted into a task and
called on the result of each