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);
}