Tuesday, March 15, 2011

General Structure of Open MP statements and all Clauses

So from now on lets stop Theory and get into some practical stuffs..!!!!

Open MP provides various Directives, Run-time routines, Environment variables to help us achieve parallelism in our application. So from now on lets begin our journey on Open MP.

First lets look at general structure of any Open MP statements :

       
    #pragma omp directive-name [clause, ...] newline
    Required for all OpenMP C/C++ directives.A valid OpenMP directive. Must appear after the #pragma omp and before any
    clauses.

    Optional. Clauses can be in any order, and repeated as necessary unless otherwise restricted.Required. Precedes the structured block which is enclosed by this directive.

Clauses :

Usually these clauses are often referred as Data-Sharing or Data-Scope Clauses. These clauses are very important while dealing with Open-MP, because these clauses are used to specify How the data is shared among the threads? Which data is private to specific thread? Which aren't?..

(Following requires prerequisite-thread, If you are not familiar with "threads" please get an overview about it here.)


Here is the list of clauses supported by most OpenMP,

                1. "private" Clause
                2. "shared" Clause
                3. "default" Clause
                4. "firstprivate" Clause
                5. "lastprivate" Clause
                6. "reduction" Clause




Clause 1 : "private" 

                   Format of this clause =>        private (list)


                  The "private"clause declares variables in its list to be private to each thread. You can specify a list of variables which you need to make it private variable for each thread. Once we specify private clause, actual work going on behind the scene is explained as follows,

  • A new object(variable) of the same data type is declared once for each thread in the team of threads to which belong.
  • All references(pointers) to the original object are replaced with references to the new object(variable) of the corresponding threads new variable(which is created in previous step)
  • Variables specified in "private" clause should be assumed to be uninitialized for each thread
  • E.g.
          int tid;
          #pragma omp parallel private(tid)
          {
                     tid = omp_get_thread_num();
                     printf("Thread ID  : %d:\n",tid);                                                    
         } /* end of parallel section */










Clause 2 : "shared"

                   Format of this clause =>        shared (list)

  • The "shared" clause declares variables in its list to be shared among all threads in the team.
  • This is one way which OpenMP allows us to share the data among multiple threads, hence achieve inter-thread communication by modifying the shared data.
  • E.g,
          int tid;
          #pragma omp parallel shared(tid)
          {
                     tid = omp_get_thread_num();
                     printf("Thread ID  : %d:\n",tid);                                                    
         } /* end of parallel section */











Clause 3 : "default"

                   Format of this clause =>        default(shared | none)

  • The "default"clause allows the user to specify a default scope for all variables in any of the parallel region, up-till the next "default" clause is encountered.
  • Specific variables can be exempted from the default using the "private", "shared", "firstprivate", "lastprivate" and "reduction" clauses.
  • Most of the C/C++ OpenMP specification allow only shared or none as a possible default, it does not allow "private" or "firstprivate" as a possible default.
  • Using none as a default requires that the programmer explicitly scope all variables.
  • E.g,
          int tid;
          #pragma omp parallel default(shared)
                      // some statements... 
          #pragma omp parallel
          {
                     tid = omp_get_thread_num(); /*here tid has default scope, which is set to "shared"*/
                     printf("Thread ID  : %d:\n",tid);                                                    
         } /* end of parallel section */













Clause 4 : "firstprivate"

                   Format of this clause =>        firstprivate(list)


  • The "firstprivate" clause combines the behavior of the "private" clause with automatic initialization of the variables in its list.
  • The variables in the field list are initialized to their respective values prior(before) to entry into the parallel or work-sharing construct
  • E.g,
          int tid = 10;
          #pragma omp parallel firstprivate(tid)
          {
                     printf("Thread ID  : %d:\n",tid);                                               
                     tid = omp_get_thread_num();
                     printf("Thread ID  : %d:\n",tid);                                                    
         } /* end of parallel section */












Clause 5 : "lastprivate"

                   Format of this clause =>        lastprivate(list)

  • The "lastprivate" clause combines the behavior of the "private" clause with a copy from the last loop iteration or section to the original variable object.
  • 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 DO section, or the team member which does the last SECTION of a SECTIONS context performs the copy with its own values.
  • This clause is not used most of the time, so if you don't get understand this clause don't worry

Clause 6 : "reduction"


                   Format of this clause =>        reduction(operator : list)


  • The "reduction" clause performs a reduction on the variables that appear in its list.
  • A private copy for each variable in the list is created for each thread. At the end of the parallel section  the reduction operation is applied to all copies of the variable in the list, and the final result is written back to the actual variable shared by other team members.
  • E.g,
          int x = 0, i;
          #pragma omp parallel for default(shared) private(i) reduction(+ : x)
          {
                      for(i = 1; i <= 10; i++)
                             x = x + 1;
          } /* end of parallel section, now the value of x is added back to actual variable shared by other team members */