Intel® C++ Compiler 16.0 User and Reference Guide
Specifies the beginning of a code block whose execution may be deferred.
#pragma omp task [clause[[,]clause]...] |
block
When a thread encounters a task construct, a task is generated from the code for the associated structured block. The encountering thread may immediately execute the task, or defer its execution. In the latter case, any thread in the team may be assigned the task.
A thread that encounters a task scheduling point within the task region may temporarily suspend the task region. By default, a task is then tied and its suspended task region can only be resumed by the thread that started its execution. However, if the untied clause is specified in a task construct, any thread in the team can resume the task region after a suspension. The untied clause is ignored in the following cases:
A task construct may be nested inside an outer task, but the task region of the inner task is not a part of the task region of the outer task.
The task construct includes a task scheduling point in the task region of its generating task, immediately following the generation of the explicit task. Each explicit task region includes a task scheduling point at its point of completion. An implementation may add task scheduling points anywhere in untied task regions.
Note that when storage is shared by an explicit task region, you must add proper synchronization to ensure that the storage does not reach the end of its lifetime before the explicit task region completes its execution.
A program must not depend on any ordering of the evaluations of the clauses of the task pragma and it must not depend on any side effects of the evaluations of the clauses. A program that branches into or out of a task region is non-conforming.
Unsynchronized use of C++ I/O statements by multiple tasks on the same unit has unspecified behavior.
Example |
---|
struct node { struct node *left; struct node *right; }; extern void process(struct node *); int depth, limit; void traverse( struct node *p ) { // When depth>limit, stop generating new tasks, and allow the // compiler to avoid creating a new data environment. if (p->left) #pragma omp task final(depth>limit) mergeable // p is firstprivate by default traverse(p->left); if (p->right) #pragma omp task final(depth>limit) mergeable // p is firstprivate by default traverse(p->right); process(p); } |
Example: Creating Tasks with Dependencies |
---|
int a; #pragma omp task depend(out:a) {} #pragma omp task depend(in:a) {} #pragma omp task depend(in:a) {} #pragma omp task depend(out:a) {} |
Above is a simple example where a four tasks with dependencies are created. The first task does not depend on any previous one. The second and third tasks depend on the first one, but not on each other. The last task depends on the second and third.
Example: Sibling Tasks with Dependencies |
---|
int a[N*B]; for (int I=0; I< N; I++ ) #pragma omp task depend(output:a[i*B:B]) Fill(&a[i*B]); for (int I = 0; I <–1; I++ ) #pragma omp task depend(inout:a[i*B:B]) depend(in:a[(i+1)*B:B]) Process(&a[i*B],&a[(I+1)*B]); for (int I = 0; I <N; I++ ) #pragma omp task depend(in:a[i*B:B]) Output(&a[i*B]); |
The tasks of the first loop will be independent of any other, as there is no previous tasks that express a dependence on the same list items. Task on the second loop will depend on two tasks from the first loop (the one that references I*B and that references (I+1)*B).
Because dependencies are constructed in a sequential order the in dependencies forces the tasks of the loop to be dependent on the task from the previous iteration to be processed. Finally, tasks from the third loop can be executed when the corresponding Process task from the second loop is executed.