Intel® Fortran Compiler 16.0 User and Reference Guide

TASK

OpenMP* Fortran Compiler Directive: Defines a task region.

!$OMP TASK [clause[[,] clause] ... ]

   block

!$OMP END TASK

clause

Is one of the following:

  • DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE)
  • DEPEND (dependence-type : list)
  • FINAL (scalar-logical-expression)

    When the scalar-logical-expression evaluates to .TRUE., it specifies that the generated task will be a final task. All task constructs encountered during execution of a final task will generate included tasks.

    Note that if a variable is used in a final clause expression of a task construct, it causes an implicit reference to the variable in all enclosing constructs

    Only a single FINAL clause can appear in the directive.

  • FIRSTPRIVATE (list)
  • IF (expression)
  • MERGEABLE

    When the generated task is an undeferred task or an included task, it specifies that the implementation may generate a merged task instead.

  • PRIVATE (list)
  • SHARED (list)
  • UNTIED

    Specifies the task is never tied to the thread that started its execution. Any thread in the team can resume the task region after a suspension. For example, during runtime, the compiler can start the execution of a given task on thread A, break execution and later resume it on thread B.

block

Is a structured block (section) of statements or constructs. You cannot branch into or out of the block (the parallel region).

The binding thread set of a TASK construct is the current team. A task region binds to the innermost enclosing parallel region.

The TASK and END TASK directive pair must appear in the same routine in the executable section of the code.

The END TASK directive denotes the end of the task.

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 these 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 directive 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 Fortran I/O statements by multiple tasks on the same unit has unspecified behavior.

Example

The following example calculates a Fibonacci number. The Fibonacci sequence is 1,1,2,3,5,8,13, etc., where the current number is the sum of the previous two numbers. If a call to function fib is encountered by a single thread in a parallel region, a nested task region will be spawned to carry out the computation in parallel.

RECURSIVE INTEGER FUNCTION fib(n)
INTEGER n, i, j
IF ( n .LT. 2) THEN  
  fib = n
ELSE
  !$OMP TASK SHARED(i)
     i = fib( n-1 )
  !$OMP END TASK
  !$OMP TASK SHARED(j)
     j = fib( n-2 )
  !$OMP END TASK
  !$OMP TASKWAIT      ! wait for the sub-tasks to
 		                   !   complete before summing
     fib = i+j
END IF
END FUNCTION

The following example generates a large number of tasks in one thread and executes them with the threads in the parallel team. While generating these tasks, if the implementation reaches the limit generating unassigned tasks, the generating loop may be suspended and the thread used to execute unassigned tasks. When the number of unassigned tasks is sufficiently low, the thread resumes execution of the task generating loop.

real*8 item(10000000)
integer i
!$omp parallel
!$omp single 	! loop iteration variable i is private
    do i=1,10000000
!$omp task
! i is firstprivate, item is shared
    call process(item(i))
!$omp end task
    end do
!$omp end single
!$omp end parallel
end 

The following example modifies the previous one to use an untied task to generate the unassigned tasks. If the implementation reaches the limit generating unassigned tasks and the generating loop is suspended, any other thread that becomes available can resume the task generation loop.

real*8 item(10000000)
!$omp parallel
!$omp single!
$omp task untied
! loop iteration variable i is private
    do i=1,10000000
!$omp task 	! i is firstprivate, item is shared
    call process(item(i))
!$omp end task
    end do
!$omp end task
!$omp end single
!$omp end parallel

The following example demonstrates four tasks with dependences:

integer :: a
 
!$omp task depend(out:a)
!$omp end task
 
!$omp task depend(in:a)
!$omp end task
 
!$omp task depend(in:a)
!$omp end task
 
!$omp task depend(out:a)
!$omp end task

In the above example, the first task does not depend on any previous one. The second and third tasks depend on the first task but not on each other. The last task depends on the second and third tasks. The following shows the dependency graph:

The following example shows a set of sibling tasks that have dependences between them:

INTEGER :: A(0:N*B-1)
 
DO I=0,N-1
!$OMP TASK DEPEND(OUT:A(I*B:(I+1)*B-1))
      CALL FILL(A(I*B:(I+1)*B-1))
!$OMP END TASK
END DO
 
DO I=1,N-1
      IB = (I-1)*B+1                 
      BB = I*B+1                      
!$OMP TASK DEPEND(INOUT:A(I*B:(I+1)*B-1)) DEPEND(IN:A((I+1)*B:(I+2)*B-1))
      CALL PROCESS(A(I*B:(I+1)*B-1), A((I+1)*B:(I+2)*B-1))
!$OMP END TASK
END DO
 
DO I=1,N
      IB = (I-1)*B+1                 
!$OMP TASK DEPEND(IN:A(I*B:(I+1)*B-1))
      CALL OUTPUT(A(I*B:(I+1)*B-1))
!$OMP END TASK
END DO

In the above example, the tasks of the first loop will be independent of any other tasks since there is no previous task that expresses a dependence on the same list items. Tasks of the second loop will depend on two tasks from the first loop. Also, because dependences are constructed in a sequential order, the IN dependences force the tasks of the second loop to be dependent on the task from the previous iteration to be processed. Finally, tasks of the third loop can be executed when the corresponding Process task from the second loop has been executed. For example, if N was 4, the following shows the dependency graph:

See Also