9.9. Data-sharing attribute clauses

Several directives accept clauses that allow a user to control the sharing attributes of variables for the duration of the region. Sharing attribute clauses applies only to variables in the lexical extent of the directive on which the clause appears. Not all of the following clauses are allowed on all directives. The list of clauses that are valid on a particular directive are described with the directive.

The following sections describe the data-sharing attribute clauses:

9.9.1. private clause

The private clause declares the variables in variable-list to be private to each thread in a team. The syntax of the private clause is as follows:

private(variable-list)

The behavior of a variable specified in a private clause is as follows:

A new object with automatic storage duration is allocated for the construct. The size and alignment of the new object are determined by the type of the variable. This allocation occurs once for each thread in the team, and a default constructor is invoked for a class object (if any) if necessary; otherwise, the initial value is indeterminate. The original object referenced by the variable has an indeterminate value upon entry to the construct, must not be modified within the dynamic extent of the construct, and has an indeterminate value upon exit from the construct.

The restrictions to the private clause are as follows:

  • A variable with a class type that is specified in a private clause must have an accessible, unambiguous default constructor.

  • A variable specified in a private clause must not have a const-qualified type unless it has a class type with a mutable member.

  • A variable specified in a private clause must not have an incomplete type or a reference type.

  • Variables that appear in the reduction clause (explained in 9.9.6, “reduction clause” on page 358) of a parallel directive cannot be specified in a private clause on a work-sharing directive that binds to the parallel construct.

9.9.2. firstprivate clause

The firstprivate clause provides a superset of the functionality provided by the private clause. The syntax of the firstprivate clause is as follows:

firstprivate(variable-list)

The firstprivate clause combines the behavior of the private clause with automatic initialization of the variables in its list. The initialization or construction happens as if it were done once per thread, prior to the thread’s execution of the construct. For a firstprivate clause on a parallel construct, the initial value of the new private object is the value of the original object that exists immediately prior to the parallel construct for the thread that encounters it. For a firstprivate clause on a work-sharing construct, the initial value of the new private object for each thread that executes the work-sharing construct is the value of the original object that exists prior to the point in time that the same thread encounters the work-sharing construct. In addition, for C++ objects, the new private object for each thread is copy constructed from the original object.

The restrictions to the firstprivate clause are as follows:

  • A variable specified in a firstprivate clause must not have an incomplete type or a reference type.

  • A variable with a class type that is specified as firstprivate must have an accessible, unambiguous copy constructor.

  • Variables that are private within a parallel region or that appear in the reduction clause of a parallel directive cannot be specified in a firstprivate clause on a work-sharing directive that binds to the parallel construct.

9.9.3. lastprivate clause

The lasprivate clause combines the behavior of the private clause with a copy from the last loop iteration or section to the original variable object. The syntax of the lastprivate clause is as follows:

lastprivate(variable-list)

The value copied back into the original variable object is obtained from the last (sequentially) iteration or section of the enclosing construct.

For example, the team member which executes the final iteration for a for section, or the team member which does the last section of a sections context performs the copy with its own values.

The restrictions to the lastprivate clause are as follows:

  • All restrictions for private apply.

  • A variable with a class type that is specified as lastprivate must have an accessible, unambiguous copy assignment operator.

  • Variables that are private within a parallel region or that appear in the reduction clause of a parallel directive cannot be specified in a lastprivate clause on a work-sharing directive that binds to the parallel construct.

9.9.4. shared clause

This clause shares variables that appear in the variable-list among all the threads in a team. All threads within a team access the same storage area for shared variables. The syntax of the shared clause is as follows:

shared(variable-list)

It is the programmer’s responsibility to ensure that multiple threads properly access shared variables (such as via critical sections).

9.9.5. default clause

The default clause allows the user to affect the data-sharing attributes of variables. The syntax of the default clause is as follows:

default(shared | none)

Specifying default(shared) is equivalent to explicitly listing each currently visible variable in a shared clause, unless it is threadprivate or const-qualified. In the absence of an explicit default clause, the default behavior is the same as if default(shared) were specified.

Specifying default(none) requires that at least one of the following must be true for every reference to a variable in the lexical extent of the parallel construct:

  • The variable is explicitly listed in a data-sharing attribute clause of a construct that contains the reference.

  • The variable is declared within the parallel construct.

  • The variable is threadprivate.

  • The variable has a const-qualified type.

Only a single default clause may be specified on a parallel directive.

9.9.6. reduction clause

This clause performs a reduction on the scalar variables that appear in variable-list, with the operator op. The syntax of the reduction clause is as follows:

reduction(op:variable-list)

A reduction is typically specified for a statement with one of the following forms:

  • x binop= expr

  • x = x op expr

  • x = expr op x (except for subtraction)

  • x++

  • ++x

  • x--

  • --x

Where:

  • x is one of the reduction variables specified in the list.

  • expr is an expression with scalar type, and it does not reference the object designated by x.

  • binop is not an overloaded operator and is one of +, *, -, &, ^, or l.

  • op is not an overloaded operator, but one of +, *, -, &, ^, l, &&, or II.

The term reduction refers to repeated updating of a variable via +,-,*, or a binary logical operator, with some other value. As will be apparent in the following, these binary operators must be commutative; division is not permitted as a reduction operator.

A reduction variable is a hybrid shared/private variable. It is initialized prior to the parallel construct, such as a shared or global variable. Within the parallel construct, it behaves as a private variable. But at the conclusion of the parallel construct, the threads’ private values are amalgamated per the designated binary operation into the original shared variable.

Let us take the following example (Example 9-5). In this program, we are printing Hello inside the nested for loop of a parallel for construct. We are also printing the number of times the word Hello is printed. Let us look at the output of the program.

Example 9-5. omp_reduction1.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    int i = 0, j = 0;
    int result = 0;

    #pragma omp parallel for private(i)
    for (i = 0; i < 3; i++) {
        for (j = i + 1; j < 4; j++) {
            printf("Hello.
");
            result = result + 1;
        }
    }

    printf("Number of times printed Hello = %d
", result);
}

When executed, the program prints the following output:

Hello.
Hello.
Hello.
Hello.
Hello.
Hello.
Number of times printed Hello = 2

Alhough the word Hello is printed six times, the result variable is counted only twice when it should have been six. Clearly, a synchronization problem exists in the reading and writing of result. This problem can be resolved in three ways.

First, as we discussed earlier, the critical directive can be used to avoid the race condition to update the result variable, as shown in Example 9-6.

Example 9-6. omp_reduction2.c
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int i = 0, j = 0;
    int result = 0;

    #pragma omp parallel for private(i)
    for (i = 0;i < 3; i++) {
        for (j = i+1; j <4; j++) {
            printf("Hello.
");
            #pragma omp critical /* To avoid race condition. */
            result = result + 1;
        }
    }
    printf("Number of times printed Hello = %d
", result);
}

When executed, the program prints the following output:

Hello.
Hello.
Hello.
Hello.
Hello.
Hello.
Number of times printed Hello = 6

Second is the use of the reduction clause. It supersedes the use of synchronization constructs in these situations. It not only prevents the race condition but also reduces the time of execution as it happens while using synchronization constructs. In Example 9-6, we may treat result as a reduction variable by modifying the parallel construct, as shown in Example 9-7 on page 361.

Example 9-7. omp_reduction3.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    int i = 0, j = 0;
    int result = 0;

    /* reduction clause is used here for result. */
    #pragma omp parallel for private(i) reduction(+:result)
    for (i = 0; i < 3; i++) {
        for (j = i + l ; j < 4; j++) {
            printf("Hello.
");
            result = result + 1;
        }
    }
    printf("Number of times printed Hello = %d
",result);
}

When executed, the program prints the following output:

Hello.
Hello.
Hello.
Hello.
Hello.
Hello.
Number of times printed Hello = 6

For the third method of solving this problem using run-time library functions, see 9.10.2, “Lock functions” on page 368.

The restrictions to the reduction clause are as follows:

  • The type of the variables in the reduction clause must be valid for the reduction operator, except that pointer types and reference types are never permitted.

  • A variable that is specified in the reduction clause must not be const-qualified.

  • Variables that are private within a parallel region or that appear in the reduction clause of a parallel directive cannot be specified in a reduction clause on a work-sharing directive that binds to the parallel construct, as shown in the following example.

#pragma omp parallel private(y)
{
    /* ERROR - private variable y cannot be specified in a reduction clause. */
    #pragma omp for reduction(+: y)
}
/* ERROR -
 * variable x cannot be specified in both a shared and a reduction clause.
 */
#pragma omp parallel for shared(x) reduction(+: x)

9.9.7. copyin clause

The copyin clause provides a mechanism to assign the same value to threadprivate variables for each thread in the team executing the parallel region. For each variable specified in a copyin clause, the value of the variable in the master thread of the team is copied, as if by assignment, to the threadprivate copies at the beginning of the parallel region. The syntax of the copyin clause is as follows:

copyin(variable-list)

The following code sample illustrates this:

/* File : omp_copyin.c */
#include <include.h>
int a, b;
#pragma omp threadprivate(a)

int main(int argc, char *argv[])
{
    /* First parallel region. */
    a = 10; /* a is initialized before the parallel region. */
    /*
     * copies the value of 'a' by assignment, if copyin is not used, 'a' is
     * visible only to the master thread.
     */
    #pragma omp parallel private(b) copyin(a)
    b = 8;
    /* Second parallel region. */
    #pragma omp parallel
    {
        printf("The value of a is %d
", a);
        printf("The value of b is %d
", b);
    }
}

When executed, the program prints the following output:

The value of a is 10
The value of b is 0
The value of a is 10
The value of b is 0
The value of a is 10
The value of b is 0
The value of a is 10
The value of b is 0

The restrictions to the copyin clause are as follows:

  • A variable that is specified in the copyin clause must have an accessible, unambiguous copy assignment operator (for C++).

  • A variable that is specified in the copyin clause must be a threadprivate variable.

9.9.8. copyprivate clause

The copyprivate clause provides a mechanism to use a private variable to broadcast a value from one member of a team to the other members. It is an alternative to using a shared variable for the value when providing such a shared variable would be difficult (for example, in a recursion requiring a different variable at each level). The copyprivate clause can only appear on the single directive. The syntax of the copyprivate clause is as follows:

copyprivate(variable-list)

Restrictions to the copyprivate clause are as follows:

  • A variable that is specified in the copyprivate clause must not appear in a private or firstprivate clause for the same single directive.

  • If a single directive with a copyprivate clause is executed in the dynamic extent of a parallel region, all variables specified in the copyprivate clause must be private in the enclosing context.

  • A variable that is specified in the copyprivate clause must have an accessible unambiguous copy assignment operator (for C++).

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

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