9.5. Work-sharing constructs

A work-sharing construct distributes the execution of the associated statement among the members of the team that encounter it. The work-sharing directives do not launch new threads, and there is no implied barrier on entry to a work-sharing construct.

OpenMP defines the three work-sharing constructs, for, sections, and single, explained in the following sections:

A work-sharing construct must be enclosed dynamically within a parallel region in order for the directive to execute in parallel. They must be executed by all members of a team or none at all. Successive work-sharing constructs must be executed in the same order by all members of a team.

9.5.1. for construct

The for directive specifies that the iterations of the loop immediately following it must be executed in parallel by the team. This assumes a parallel region has already been initiated; otherwise, it executes in serial on a single processor.

The syntax for the for construct is as follows:

#pragma omp for [clause[[,] clause] ... ] new-line
   for-loop

The clause is one of the following:

  • private(variable-list)

  • firstprivate(variable-list)

  • lastprivate(variable-list)

  • reduction(operator: variable-list)

  • ordered

  • schedule(kind[, chunk_size])

  • nowait

Let us discuss a few of the clauses in brief. For a detailed description of the other clauses, see 9.9, “Data-sharing attribute clauses” on page 355.

The ordered clause must be present when ordered directives bind to for construct.

The schedule clause describes how iterations of the loop are divided among the threads in the team. The schedule kind may be one of the following:

staticLoop iterations are divided into pieces of chunk_size and then statically assigned to threads. If chunk is not specified, the iterations are evenly (if possible) divided contiguously among the threads.
dynamicLoop iterations are divided into pieces of size chunk_size and dynamically scheduled among the threads; when a thread finishes one chunk, it is dynamically assigned another. The default chunk_size is 1.
guidedThe chunk_size is exponentially reduced with each dispatched piece of the iteration space. The chunk_size specifies the minimum number of iterations to dispatch each time. The default chunk_size is 1.
runtimeThe scheduling decision is deferred until run time by the environment variable OMP_SCHEDULE. It is not allowed to specify a chunk_size for this clause.

If the nowait clause is specified, then threads do not synchronize at the end of the parallel loop. Threads proceed directly to the next statements after the loop.

The restrictions to the for directive are as follows:

  • The for loop must be a structured block, and, in addition, its execution must not be terminated by a break statement.

  • The values of the loop control expressions of the for loop associated with a for directive must be the same for all the threads in the team.

  • The for loop iteration variable must have a signed integer type.

  • Only a single schedule clause can appear on a for directive.

  • Only a single ordered clause can appear on a for directive.

  • Only a single nowait clause can appear on a for directive.

  • The value of the chunk_size expression must be the same for all threads in the team.

The for directive requires that the for loop must have canonical form. In the OpenMP specifications, the canonical form allows the number of loop iterations to be computed on entry to the loop. For example, the following code fragment:

#pragma omp for reduction(+:overallsum)
for (n = 0; n != ARRSIZE; n++) }
    overallsum += array[n];
}

would yield the following error when it is compiled:

" 1506-818 (S) Controlling expression of the for loop is not in the canonical
form."

because the loop test expression n != ARRSIZE, should be replaced by

n < ARRSIZE.

The example program shown in Example 9-2 on page 343 illustrates the use of the for directive. In this example, the arrays a, b, and total are shared by all threads. The variable i is private to each thread and each thread has its own copy of it. The iterations of the for is distributed dynamically in chunk_size pieces. The nowait clause ensures that threads will not synchronize upon completing their individual pieces of work.

Example 9-2. omp_ws_for.c
#include <stdio.h>
#define CHUNKSIZE 5
#define N         10

int main(int argc, char *argv[])
{
    int i, chunk_size;
    float a[N], b[N], total[N];

    /* Some initializations. */
    for (i = 0; i < N; i++)
        a[i] = b[i] = i * 1.0;
    chunk_size = CHUNKSIZE;

    #pragma omp parallel shared(a,b,total,chunk_size) private(i)
    {
        #pragma omp for schedule(dynamic,chunk_size) nowait
        for (i=0; i < N; i++)
            total[i] = a[i] + b[i];
    }  /* end of pragma omp parallel for. */
    for (i = 0; i < N; i++)
        printf("The total value is = %f
", total[i]);
}

When executed, the program prints the output shown in Example 9-3.

Example 9-3. Output fromomp_ws_for.c
The total value is = 0.000000
The total value is = 2.000000
The total value is = 4.000000
The total value is = 6.000000
The total value is = 8.000000
The total value is = 10.000000
The total value is = 12.000000
The total value is = 14.000000
The total value is = 16.000000
The total value is = 18.000000

9.5.2. sections construct

The sections directive identifies a non-iterative work-sharing construct that specifies a set of constructs that are to be divided among threads in a team. Each section is executed once by a thread in the team. The syntax of the sections directive is as follows:

#pragma omp sections [clause[[,] clause] ...] new-line
{
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-line
      structured-block ]
   ...
}

The clause is one of the following:

  • private(variable-list)

  • firstprivate(variable-list)

  • lastprivate(variable-list)

  • reduction(operator: variable-list)

  • nowait

Each section is preceded by a section directive, although the section directive is optional for the first section. The section directives must appear within the lexical extent of the sections directive. There is an implicit barrier at the end of a sections construct, unless a nowait is specified.

Restrictions to the sections directive are as follows:

  • A section directive must not appear outside the lexical extent of the sections directive.

  • Only a single nowait clause can appear on a sections directive.

The example shown in Example 9-4 on page 345 illustrates the use of the sections directive. The program is a slight modification of Example 9-2 on page 343, which illustrated the use of the for directive. The first N/2 iterations of the for loop will be distributed to the first thread, and the rest will be distributed to the second thread. When each thread finishes its block of iterations, it proceeds with whatever code comes next (nowait).

Example 9-4. omp_ws_section.c
#include <stdlib.h>
#define N     10

int main(int argc, char *argv[])
{
    int i;
    float a[N], b[N], total[N];

    /* Some initializations. */
    for (i = 0; i < N; i++)
        a[i] = b[i] = i * 1.0;

    #pragma omp parallel shared(a,b,total) private(i)
    {
        #pragma omp sections nowait
        {
            #pragma omp section
            for (i = 0; i < N / 2; i++)
                total[i] = a[i] + b[i];
            #pragma omp section
            for (i = N / 2; i < N; i++)
                total[i] = a[i] + b[i];
        } /* end of pragma omp sections nowait. */
    } /* end of pragma omp parallel shared. */
    for (i = 0; i < N; i++)
        printf("The total value is = %f
", total[i]);
}

When executed, the program prints the same output shown in Example 9-3 on page 343.

9.5.3. single construct

The single directive identifies a construct that specifies that the associated structured block is executed by only one thread in the team (not necessarily the master thread). This may be useful when dealing with sections of code that are not thread safe (such as I/O). The syntax of the single directive is as follows:

#pragma omp single [clause[[,] clause] ...] new-line
   structured-block

The clause is one of the following:

  • private(variable-list)

  • firstprivate(variable-list)

  • copyprivate(variable-list)

  • nowait

Restrictions to the single directive are as follows:

  • Only a single nowait clause can appear on a single directive.

  • The copyprivate clause must not be used with the nowait clause.

  • single constructs cannot be nested within sections construct.

The following example program illustrates the use of a single construct:

/* File : omp_ws_single.c */
#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("Before forking a parallel region.
");

    /* Fork a team of threads giving them their own copies of variables. */
    #pragma omp parallel
    {
        /* The printf() line is executed only once by any of the thread. */
        #pragma omp single
        printf("Before printing Hello World...
");

        /* The printf() line is executed by all the thread. */
        printf("Hello World.
");

        /* The printf() line is executed only once by any of the thread. */
        #pragma omp single
        printf("Done.. Leaving.
");
    }
}

If executed, the program prints the following output:

Before forking a parallel region.
Before printing Hello World...
Hello World.
Done.. Leaving.
Hello World.
Hello World.

In the above output, the statements in the single construct were executed only once by one of the threads from the team.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset