Tasks
TaskList
The TaskList
class implements methods to build and execute a set of
tasks with associated dependencies. The class implements a few public
facing member functions that provide useful functionality for downstream
apps:
AddTask
AddTask
is a templated variadic function that takes the task
function to be executed, the task dependencies (see TaskID
below),
and the arguments to the task function as it’s arguments. All arguments
are captured by value in a lambda for later execution.
When adding functions that are non-static class member functions, a
slightly different interface is required. The first argument should be
the class-name-scoped name of the function. For example, for a function
named DoSomething
in class SomeClass
, the first argument would
be &SomeClass::DoSomething
. The second argument should be a pointer
to the object that should invoke this member function. Finally, the
dependencies and function arguments should be provided as described
above.
Examples of both AddTask
calls can be found in the advection example
here.
AddIteration
AddIteration
provides a means of grouping a set of tasks together
that will be executed repeatedly until stopping criteria are satisfied.
AddIteration
returns an IterativeTasks
object which provides
overloaded AddTask
functions as described above, but internally
handles the bookkeeping necessary to maintain the association of all the
tasks associated with the iterative process. A special function
SetCompletionTask
, which behaves identically to AddTask
, allows
a task to be defined that evaluates the stopping criteria. The maximum
number of iterations can be controlled through the SetMaxIterations
member function and the number of iterations between evaluating the
stopping criteria can be set with the SetCheckInterval
function.
DoAvailable
DoAvailable
loops over the task list once, executing all tasks whose
dependencies are satisfied. Completed tasks are removed from the task
list.
TaskID
The TaskID
class implements methods that allow Parthenon to keep
track of tasks, their dependencies, and what remains to be completed.
The main way application code will interact with this object is as a
returned object from TaskList::AddTask
and as an argument to
subsequent calls to TaskList::AddTask
as a dependency for other
tasks. When used as a dependency, TaskID
objects can be combined
with the bitwise or operator (|
) to specify multiple dependencies.
TaskRegion
TaskRegion
is a lightweight class that wraps
std::vector<TaskList>
, providing a little extra functionality.
During task execution (described below), all task lists in a
TaskRegion
can be operated on concurrently. For example, a
TaskRegion
can be used to construct independent task lists for each
MeshBlock
. Occasionally, it is useful to have a task not be
considered complete until that task completes in all lists of a region.
For example, a global iterative solver cannot be considered complete
until the stopping criteria are satisfied everywhere, which may require
evaluating those criteria in tasks that live in different lists within a
region. An example of this use case is
shown here. The mechanism
to mark a task so that dependent tasks will wait until all lists have
completed it is to call AddRegionalDependencies
, as shown in the
Poisson example.
TaskCollection
A TaskCollection
contains a
std::vector<TaskRegion>
, i.e. an ordered list of TaskRegion
s.
Importantly, each TaskRegion
will be executed to completion before
subsequent TaskRegion
s, introducing a notion of sequential
execution and enabling flexibility in task granularity. For example, the
following code fragment uses the TaskCollection
and TaskRegion
abstractions to express work that can be done asynchronously across
blocks, followed by a bulk synchronous task involving all blocks, and
finally another round of asynchronous work.
TaskCollection tc;
TaskRegion &tr1 = tc.AddRegion(nmb);
for (int i = 0; i < nmb; i++) {
auto task_id = tr1[i].AddTask(dep, foo, args, blocks[i]);
}
{
TaskRegion &tr2 = tc.AddRegion(1);
auto sync_task = tr2[0].AddTask(dep, bar, args, blocks);
}
TaskRegion &tr3 = tc.AddRegion(nmb);
for (int i = 0; i < nmb; i++) {
auto task_id = tr3[i].AddTask(dep, foo, args, blocks[i]);
}
A diagram illustrating the relationship between these different classes is shown below.
TaskCollection
provides two member functions, AddRegion
and
Execute
.
AddRegion
AddRegion
simply adds a new TaskRegion
to the back of the
collection and returns it as a reference. The integer argument
determines how many task lists make up the region.
Execute
Calling the Execute
method on the TaskCollection
executes all
the tasks that have been added to the collection, processing each
TaskRegion
in the order they were added, and allowing tasks in
different TaskList
s but the same TaskRegion
to be executed
concurrently.