Index Space Tasks
This example illustrates how to launch a large number of non-interfering tasks in Legion using a single index space task launch. (We discuss what it means to be non-interfering in a later example.) It also describes the basic Legion types for arrays, domains, and points and give examples of how they work.
Rectangles and Domains
To aid in describing structured data, Legion supports
a Rect type which is used to describe an arbitrary-dimensional
dense arrays of points. Rect types are templated on the number
of dimensions that they describe as well as a coordinate type
which defaults to 64 bit signed integers. To specify a Rect a
user gives two Point objects which specify the lower and
upper bounds in all dimensions respectively. Similar to
the Rect type, a Point type is templated on the
number of dimensions it stores and the coordinate type.
In this example we create a 1-D Rect for which we’ll
launch an array of tasks with one task per point (line 29).
Note that the bounds on Rect objects are inclusive.
It can be useful to be able to refer to a rectangle where the number
of dimensions is not known at compile time. The Domain class is used
for this purpose. While we do not use Domain in this example, and
Legion runtime calls almost always accept Rect, you may see this
in Legion code in the wild.
Argument and Future Maps
When launching a large set of tasks in a single
call, we may want to pass different arguments to each
task. ArgumentMap types allow the user to pass
different TaskArgument values to tasks associated
with different points. ArgumentMap types do not
need to specify arguments for all points. Legion
is intelligent about only passing arguments to tasks
which have arguments assigned. In this example we
create an ArgumentMap (line 31) and then pass in
different integer arguments associated with each
point (lines 32-35). ArgumentMap objects copy
their values on calls to set_point, but the actual
ArgumentMap itself will not be copied until the
task is launched. Legion is smart about copying
ArgumentMap values so that data is not copied
unnecessarily.
Legion also provides FutureMap types as a mechanism
for managing the many return values that are returned
from an index space task launch. FutureMap objects
store a future value for every point in the index
space task launch. Applications can either wait on the
FutureMap for all tasks in the index space launch
to complete, or it can extract individual Future
objects associated with specific points in the
index space task launch.
In this example we wait for all the point tasks in
the index space task launch using the wait_all_results
method (line 43). We then extract individual future
values for each point and check that each returned
value matches the expected result (lines 45-55).
Even though we use the get_result method on the
Future objects, we know that the values will
be immediately ready because we already waited
for all the point tasks to complete.
Index Space Launches
Index space tasks are launched in a similar manner to
individual tasks using a launcher object which has
the type IndexTaskLauncher (lines 37-40). IndexTaskLauncher objects take
some of the same arguments as TaskLauncher objects
such as the ID of the task to launch and a TaskArgument
which is passed by value as a global argument to all
the points in the index space launch. The IndexTaskLauncher
objects also take the additional arguments of an
ArgumentMap and a Rect which describes the set
of tasks to create (one for each point). Index space
tasks are launched the same as single tasks, but
return a FutureMap (line 42). Just like individual tasks,
index space task launches are performed asynchronously.
Index Space Tasks
Index space task variants are registered the same as single tasks.
Additional fields on the Task object are defined
when executing as an index space task. First, the
index_space_task field is set to true. Next,
index space tasks can find the point they are responsible
for executing in the index_point field. The index_point
field is a DomainPoint which is a type-erased form of a
Point type. Lines 61-62 show how a task can make
use of the index_point field. Finally, the local_arglen
and local_arg fields contain the TaskArgument values
passed in to the ArgumentMap for the given task’s
point (if any existed). In lines 63-65, the application
does some simple computation on the input value and then
returns it. The resulting value is set in the Future
for the corresponding point in the FutureMap.
Next Example: Hybrid Model
Previous Example: Tasks and Futures
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <cstdio>
#include <cassert>
#include <cstdlib>
#include "legion.h"
using namespace Legion;
enum TaskIDs {
TOP_LEVEL_TASK_ID,
INDEX_SPACE_TASK_ID,
};
void top_level_task(const Task *task,
const std::vector<PhysicalRegion> ®ions,
Context ctx, Runtime *runtime) {
int num_points = 4;
const InputArgs &command_args = Runtime::get_input_args();
for (int i = 1; i < command_args.argc; i++) {
if (command_args.argv[i][0] == '-') {
i++;
continue;
}
num_points = atoi(command_args.argv[i]);
assert(num_points > 0);
break;
}
printf("Running hello world redux for %d points...\n", num_points);
Rect<1> launch_bounds(0,num_points-1);
ArgumentMap arg_map;
for (int i = 0; i < num_points; i++) {
int input = i + 10;
arg_map.set_point(i, TaskArgument(&input, sizeof(input)));
}
IndexTaskLauncher index_launcher(INDEX_SPACE_TASK_ID,
launch_bounds,
TaskArgument(NULL, 0),
arg_map);
FutureMap fm = runtime->execute_index_space(ctx, index_launcher);
fm.wait_all_results();
bool all_passed = true;
for (int i = 0; i < num_points; i++) {
int expected = 2*(i+10);
int received = fm.get_result<int>(i);
if (expected != received) {
printf("Check failed for point %d: %d != %d\n", i, expected, received);
all_passed = false;
}
}
if (all_passed)
printf("All checks passed!\n");
}
int index_space_task(const Task *task,
const std::vector<PhysicalRegion> ®ions,
Context ctx, Runtime *runtime) {
assert(task->index_point.get_dim() == 1);
printf("Hello world from task %lld!\n", task->index_point.point_data[0]);
assert(task->local_arglen == sizeof(int));
int input = *((const int*)task->local_args);
return (2*input);
}
int main(int argc, char **argv) {
Runtime::set_top_level_task_id(TOP_LEVEL_TASK_ID);
{
TaskVariantRegistrar registrar(TOP_LEVEL_TASK_ID, "top_level");
registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
Runtime::preregister_task_variant<top_level_task>(registrar, "top_level");
}
{
TaskVariantRegistrar registrar(INDEX_SPACE_TASK_ID, "index_space_task");
registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
registrar.set_leaf();
Runtime::preregister_task_variant<int, index_space_task>(registrar, "index_space_task");
}
return Runtime::start(argc, argv);
}




