Programming applications

This page presents how to program OpenMP tasks based applications for distributed systems using the OmpCluster runtime.

For more details about the OpenMP directives, you can consult the specification of OpenMP.

OmpCluster relies on the application programming interface defined by OpenMP. It uses directives to program remote processes running on computer clusters with distributed memory architectures.

Between the Device Constructs that we use in this project, we have:

Execute code on a target device

omp target [clause[[,] clause],...] structured-block
omp declare target  [function-definitions-or-declarations]

Manage the device data environment

This construction allows you to transfer data between the host (the head process) and the devices (the worker nodes), where the target regions will be executed.

map ([map-type:] list) map-type := alloc | tofrom | to | from | release | delete

If map-type is to or tofrom, this new item is initialized with the value of the original list item in list in the host data environment.

#pragma omp target           \
  map(to:...)                \
  map(tofrom:...)
{
  ...
}

If map-type is from or alloc, the initial value of the list item in the device data environment is undefined.

#pragma omp target            \
  map(from:...)               \
  map(alloc:...)
{
  ...
}

Asynchronous target task

nowait clause eliminates the implicit barrier so the parent task can make progress even if the target task is not yet completed. By default, an implicit barrier exists at the end of the target construct, which ensures the parent task cannot continue until the target task is completed.

#pragma omp target nowait
{
  ...
}

Task dependencies

depend(dependence-type:list) establishes scheduling dependencies between the target task and sibling tasks that share list items. The dependence-type can be in, out, or inout.

If dependence-type is in or inout, a scheduling dependence for the target task on the sibling task is created. Where the task that we are creating depends that the data inserted in the clause in or inout is ready.

#pragma omp target nowait          \
  depend(in:...)                   \
  depend(inout:...)
{
  ...
}

If dependence-type is out or inout, a scheduling dependence for the target task on the sibling task is created. Where the task we are creating will generate the outputs described in the out and inout clause.

#pragma omp target nowait          \
  depend(out:...)                  \
  depend(inout:...)
{
  ...
}

Data environment

firstprivate(list) declares the data variables in list to be private to the target task and shared by every thread team that runs the region. A new item is created for each list item that is referenced by the target task. Each new data variable is initialized with the value of the original variable at the time the target construct is encountered.

#pragma omp target nowait          \
  firstprivate(list)
{
  ...
}

Asynchronous target data task

Target data tasks are basically tasks dedicated to communication between the head process and the worker processes.

Those type of tasks allows the programmer to describe data to be send to the worker processes with a larger life scope. Indeed, the data will stay alive in the worker processes between the enter and the exit directive. By using those, the OMPC runtime will be able to optimize the communication within the worker processes.

Here is an example:

It is important to point out that every target tasks which is going to use a data sent by a target data task must use that first position of the array as a dependency. This differs from the OpenMP standard but is compulsory for the OMPC runtime to be able to keep track of the data used by the target tasks and manage the communication between worker processes correctly.

If the dependencies are not set correctly (pointing to the first position of the value) the execution of the program will most probably fail on a segfault.