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 TaskRegions. Importantly, each TaskRegion will be executed to completion before subsequent TaskRegions, 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.

Task Diagram

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 TaskLists but the same TaskRegion to be executed concurrently.