OpenMP in Fortran

OpenMP stellt eine einfache Möglichkeiten dar, Code zu parallelisieren. Dabei parallelisiert der Compiler automatisch Schleifen etc, mithilfe von mehreren Threads.

Parallel Do

Eine Do-Schleife (also eine Zählschleife) lässt sich durch OpenMP parallelisieren, wenn die Zählvariable im Schleifenkörper nicht verändert wird. Bspw. ist es möglich folgende Schleife zu parallelisieren:

do.f90
program main
  integer, parameter :: n = 1000000
  integer :: k
  integer,dimension(n) :: value
 
  do k=1, n, +1
    value(k) = k**k
  enddo
 
  write (*,*) (value(k), k=1, n)
end program

Hier werden also Quadratzahlen der Zahlen von 1 bis 1000000 berechnet. Nun geben wir dem Compiler zu verstehen, dass die do-Schleife parallel ausgeführt werden soll:

do_omp.f90
program main
  integer, parameter :: n = 1000000
  integer :: k
  integer,dimension(n) :: value
 
!$omp parallel do
  do k=1, n, +1
    value(k) = k**k
  enddo
!$omp end parallel do
 
  write (*,*) (value(k), k=1, n)
end program

Übersetzen wir den Code:

gfortran do_omp.f90 -fopenmp

Und führen aus, erhalten wir folgende Zeiten:

$ gfortran do.f90
$ time ./a.out > /dev/null

real	0m0.303s
user	0m0.289s
sys	0m0.011s
$ gfortran do_omp.f90 -fopenmp
$ time ./a.out > /dev/null

real	0m0.288s
user	0m0.354s
sys	0m0.013s

Eine Einschätzung, warum der Geschwindigkeitsgewinn so gering sein könnten, finden Sie unten.

Reduktion

Neben vielen weiteren Funktionen von OpenMP gibt es noch die Möglichkeit eine Reduktion anzugeben, oder Variablen privat pro Thread oder shared zu setzen. Hier wird eine Summe über alle Quadratzahlen berechnet:

do_omp_summe.f90
program main
  integer, parameter :: n = 1000000
  integer :: k
  integer :: summe
 
!$omp parallel do reduction(+:summe) default(shared), private(k)
  do k=1, n, +1
    summe = summe + k**k
  enddo
!$omp end parallel do
 
  write (*,*) summe
end program

Warum sind die gezeigten Beispiele so langsam?

OpenMP arbeitet mit Threads, dh. aus dem Code, den Sie dem Compiler übergeben, erzeugt dieser Threads. Die Threaderzeugung benötigt eine Weile. Wenn also jeder Thread wenig Arbeit zu machen hat, dann überwiegt die Erzeugungszeit der Threads, gegenüber der Ersparnis der Berechnungszeit. Bei größeren Beispielen sollen Sie Laufzeitverringerungen bemerken.