Вперед: 1.4.2. Программирование для систем с распределенной памятью
Назад: 1.4.1.3. Многопоточное программирование на языке Java
К содержанию: Оглавление


1.4.1.4. Программирование средствами OpenMP

Прикладной программный интерфейс OpenMP (API OpenMP) предоставляет средства для разработки переносимых масштабируемых параллельных программ для систем с общей памятью [12]. Первая спецификация 1.0 была выпущена в 1997 году для языка Фортран и значительно обновлена в спецификации 1.1 в 1999 году. В 1998 г. была выпущена спецификация для языка Си. Текущая версия стандарта - версия 2.5 выпущена в 2005 году. В этой версии объединена поддержка языков Фортран (77 и 90) и С/C++. До этой версии отдельно выпускались интерфейсы для Фортрана и для Си. Разработкой стандарта занимается организация OpenMP ARB. В настоящее время для открытого обсуждения опубликована версия стандарта 3.0, однако не все компиляторы поддерживают ее в полном объеме. Поддержка OpenMP включена во многие коммерческие компиляторы (Intel, SUN, Portland Group) и, начиная с версии 4.1, поддерживается в наборе открытых компиляторов Gnu Linux. По своей сути OpenMP является высокоуровневой надстройкой над Pthread, призванной облегчить разработку многопоточных приложений. OpenMP реализует идею "инкрементального распараллеливания", когда для многопроцессорной системы не пишется специальная параллельная программа, а в обычную последовательную программу добавляются распараллеливающие OpenMP директивы, которые игнорируются обычным компилятором. На примере вычисления числа Pi; покажем, как преобразуется последовательная программа на Фортране в параллельную версию этой программы. Для этого перепишем сначала последовательную программу на язык Фортран.

Программа pi.f

program pi  
   integer n, i
   double precision d, s, x, pi
   write(*,*) 'n?'
   read(*,*) n
   d = 1.0/n
   s = 0.0
   do i=1, n
    x = (i-0.5)*d
    s = s+4.0/(1.0+x*x)
   enddo
   pi = d*s 
   write(*,100) pi
 100 format(' pi = ', f20.15)
   stop
   end

Параллельная версия программы, разработанная с использованием средств OpenMP, будет иметь вид:

Программа pi_omp.f

program pi_omp
   integer n, i
   double precision d, s, x, pi
   write(*,*) 'n?'
   read(*,*) n
   d = 1.0/n
   s = 0.0
!$OMP PARALLEL PRIVATE(x), SHARED(d)
!$OMP& REDUCTION(+: s)
!$OMP DO
   do i = 1, n
     x = (i-0.5)*d
     s = s+4.0/(1.0+x*x)
   end do
!$OMP END DO
!$OMP END PARALLEL 
   pi = d*s
   write(*,100) pi
 100  format(' pi = ', f20.15)
   stop
   end

Как видно из листинга программы pi_omp.f, не потребовалось ни какой переделки самой программы. Для распараллеливания этой программы в нее всего лишь добавлены строки, содержащие OpenMP директивы. Каждая OpenMP директива начинается с комбинации символов "!$OMP", за которой следует собственно директива, и, возможно, дополнительные ключи. Например, директивы "!$OMP PARALLEL" и "!$OMP END PARALLEL" выделяют параллельную область программы, и весь код, находящийся внутри этого блока, будет выполняться всеми нитями. При входе в эту область в дополнение к основной нити будет порождено N-1 дополнительных нитей. Общее число нитей определяется переменной окружения OMP_NUM_THREADS. Создание параллельной области само по себе не приводит к разделению работы между нитями. Это делается специальными директивами "!$OMP DO" и "!$OMP END DO", которые определяют циклы, которые должны быть выполнены параллельно.

В пределах параллельной области данные могут быть или приватными для каждой нити, или общими для всех нитей. По умолчанию, все статические переменные являются общими (исключение составляет переменная параллельного цикла, которая всегда является приватной). Напротив, все динамические переменные по умолчанию являются приватными. В нашем примере не желательно, чтобы общей была переменная x, поэтому используется директива ''!$OMP PARALLEL PRIVATE (x)'', чтобы сделать ее приватной.

В программах на языках C/C++ вместо спецкомментариев используются директивы компилятора "#pragma omp". Распараллеливание применяется к for-циклам, для этого используется директива "#pragma omp for". В параллельных циклах запрещается использовать оператор break. Типы и функции OpenMP определены во включаемом файле <omp.h>.

При компиляции OpenMP программы компилятор автоматически переведет OpenMP программу в многопотоковую программу с вызовами функций из библиотеки Pthread. Приведем пример OpenMP программы на языке Си.

Программа pi_omp.c

#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <omp.h>

main(int argc, char **argv)
{
 long int n, i;
 int nt;
 double d, s, x, pi, time;
 n = atoi(argv[1]);
 d = 1.0/n;
 s = 0.0;
#pragma omp parallel
{
#pragma omp single
{
 nt = omp_get_num_threads();
}
#pragma omp for private(x) firstprivate(d) reduction(+:s)
 for (i=1; i<=n; i++){
  x = (i-0.5)*d;
  s += 4.0/(1.0+x*x);
 }
}
 pi = d*s;
 printf("pi=%.15f\n", pi);
 printf("eps=%e\n",fabs(M_PI-pi));
 printf("num threads = %d\n",nt);
}

Более детально прикладной программный интерфейс OpenMP рассматривается в Главе 3.



Вперед: 1.4.2. Программирование для систем с распределенной памятью
Назад: 1.4.1.3. Многопоточное программирование на языке Java
К содержанию: Оглавление