Setting Up OpenMP in Fortran

OpenMP (Open Multi-Processing) is an application programming interface (API) that supports multi-platform shared-memory parallel programming in C, C++, and Fortran. It is used to exploit parallelism in multi-core processors, allowing Fortran programs to execute more efficiently by utilizing multiple processor cores. This can lead to significant performance improvements, especially for computationally intensive tasks.

Before you can use OpenMP in Fortran, you must ensure that your compiler supports it and that you enable OpenMP-specific flags during compilation. This guide will walk you through the process of setting up OpenMP in Fortran, including installing the necessary tools, configuring the compiler, and understanding OpenMP directives.

What is OpenMP?

OpenMP is a set of compiler directives, libraries, and environment variables that allow developers to write parallel programs. It simplifies the task of parallelizing programs by enabling the programmer to specify parallel sections of code using simple compiler directives.

The key feature of OpenMP is that it provides shared-memory parallelism, which means that all threads created during parallel execution share the same address space. OpenMP works by breaking the task into smaller chunks, which are then distributed among multiple threads to be executed simultaneously.


Step 1: Ensure Compiler Supports OpenMP

Common Fortran Compilers Supporting OpenMP

Several popular Fortran compilers support OpenMP. Among them are:

  • Intel Fortran Compiler (ifort)
  • GNU Fortran Compiler (gfortran)
  • Cray Fortran Compiler (ftn)

Most modern versions of these compilers have OpenMP support built-in. However, it’s always a good idea to ensure that your compiler version is up-to-date and supports OpenMP.

Checking OpenMP Support

If you’re not sure whether your Fortran compiler supports OpenMP, you can check the documentation for your specific compiler. Most compilers will have specific flags to enable OpenMP. Below are the flags used by popular Fortran compilers:

  • Intel Fortran: -qopenmp
  • GNU Fortran: -fopenmp
  • Cray Fortran: -fopenmp (or --openmp)

You can also check for OpenMP support by running a simple Fortran program with OpenMP directives and verifying if it compiles successfully.


Step 2: Enable OpenMP Support in the Compiler

To enable OpenMP in your Fortran program, you need to use specific flags during compilation. Here’s how you can compile a Fortran program with OpenMP support for the most commonly used compilers.

2.1 Intel Fortran Compiler (ifort)

For the Intel Fortran compiler, you can enable OpenMP with the -qopenmp flag. Here’s the syntax:

ifort -qopenmp program.f90

The -qopenmp flag tells the Intel Fortran compiler to recognize and process OpenMP directives in the program.

2.2 GNU Fortran Compiler (gfortran)

For the GNU Fortran compiler, the flag -fopenmp is used to enable OpenMP:

gfortran -fopenmp program.f90

This flag directs the GNU Fortran compiler to compile the program with OpenMP support.

2.3 Cray Fortran Compiler (ftn)

For Cray Fortran, the flag -fopenmp (or sometimes --openmp) enables OpenMP support:

ftn -fopenmp program.f90

This tells the Cray Fortran compiler to recognize the OpenMP directives in the program.


Step 3: Writing OpenMP Directives in Fortran

Once OpenMP is enabled in your compiler, you can start using OpenMP directives in your Fortran code. OpenMP directives are usually added as comments with specific syntax, which the compiler will recognize and handle accordingly during parallel execution.

Here are the most commonly used OpenMP directives in Fortran:

3.1 Parallel Region

The !$OMP PARALLEL directive begins a parallel region, instructing the compiler to execute the code inside it in parallel. All threads in the parallel region execute the code concurrently.

Example:

program parallel_example
  implicit none
  integer :: i, result

  ! Initialize result
  result = 0

  ! Parallelize the loop
  !$OMP PARALLEL DO REDUCTION(+:result)
  do i = 1, 100
result = result + i
end do !$OMP END PARALLEL DO print *, "Result = ", result end program parallel_example

In this example, the PARALLEL DO directive parallelizes the loop that calculates the sum of integers from 1 to 100. The REDUCTION clause ensures that the variable result is updated correctly by each thread.

3.2 Parallelizing a Section of Code

You can also parallelize specific sections of code using !$OMP PARALLEL without the DO loop if you want to execute a block of code in parallel:

program parallel_section
  implicit none
  integer :: a, b, result

  a = 5
  b = 10
  result = 0

  !$OMP PARALLEL
  result = a + b
  !$OMP END PARALLEL

  print *, "Result = ", result
end program parallel_section

In this case, the addition of a and b is done in parallel, even though it is just a simple operation. Each thread in the parallel region will perform the addition independently.

3.3 Synchronization with Critical Sections

In many cases, you may need to ensure that only one thread can execute a certain block of code at a time. This can be achieved using the !$OMP CRITICAL directive, which creates a critical section where only one thread can execute the code at a time.

program critical_example
  implicit none
  integer :: i, result

  result = 0

  !$OMP PARALLEL DO
  do i = 1, 100
!$OMP CRITICAL
result = result + i
!$OMP END CRITICAL
end do !$OMP END PARALLEL DO print *, "Result = ", result end program critical_example

The CRITICAL section ensures that the update to the result variable is done in a way that avoids race conditions, meaning only one thread can access the variable at any given time.

3.4 Using Private Variables

OpenMP allows you to declare private variables inside a parallel region. These private variables are local to each thread, meaning that each thread will have its own copy of the variable.

program private_example
  implicit none
  integer :: i, result

  result = 0

  !$OMP PARALLEL PRIVATE(i)
  do i = 1, 100
result = result + i
end do !$OMP END PARALLEL print *, "Result = ", result end program private_example

In this case, the variable i is declared private, ensuring that each thread has its own copy of i and does not interfere with the others.


Step 4: Compiling and Running the Program

Once your code is written with OpenMP directives, the next step is to compile and run your program. The exact command depends on the compiler you’re using. Below are examples for each major compiler:

4.1 Intel Fortran (ifort)

To compile and run an Intel Fortran program with OpenMP, use the following commands:

ifort -qopenmp program.f90 -o program
./program

4.2 GNU Fortran (gfortran)

For a program compiled with the GNU Fortran compiler:

gfortran -fopenmp program.f90 -o program
./program

4.3 Cray Fortran (ftn)

For Cray Fortran:

ftn -fopenmp program.f90 -o program
./program

Step 5: Optimizing Performance

Once you have your program running in parallel, it’s important to analyze its performance. OpenMP provides a variety of ways to optimize parallel execution, including:

  • Dynamic Scheduling: You can dynamically allocate work to threads during execution, which can help with load balancing.
  • Work-sharing Constructs: You can use directives like !$OMP DO, !$OMP SECTION, and others to divide work efficiently between threads.
  • Environment Variables: OpenMP allows you to control aspects of parallelism, such as the number of threads used, via environment variables like OMP_NUM_THREADS.

Example of setting the number of threads:

export OMP_NUM_THREADS=4

This command sets the number of threads to 4. You can adjust this number based on your machine’s capabilities and the nature of your program.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *