Прикладной программный интерфейс 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; покажем, как преобразуется последовательная программа на Фортране в параллельную версию этой программы. Для этого перепишем сначала последовательную программу на язык Фортран.
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, будет иметь вид:
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 программы на языке Си.
#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.