Модели программирования OpenMP и Intel® CilkTM Plus
Программирование с OpenMP (Open MultiProcessing)
Модель программы
- программа состоит из последовательных и параллельных секций;
- в начальный момент времени порождается основная нить, выполняющая последовательные секции программы;
- при входе в параллельную секцию программы выполняется операция fork, порождающая набор нитей;
- каждая нить имеет свой уникальный числовой идентификатор (0 для мастер-нити). Все параллельные нити исполняют один код;
- при выходе из параллельной секции выполняется операция join, завершающая выполнение всех нитей кроме главной.
OpenMP
Pro
- возможность пошагового распараллеливания;
- переносимость;
- высокоуровневое программирование;
- поддержка языков Fortran и C/C++;
- поддержка модели параллелизма данных.
Contra
- масштабируемость ограничена архитектурой исполнения;
- программист должен думать не только о том, ЧТО должно выполняться параллельно, но и КАК;
- побочные эффекты использования глобальных переменных.
Структура
- Директивы компилятора - используются для создания потоков, распределения работы между потоками и их синхронизации. Директивы включаются в исходный текст программы.
- Подпрограммы библиотеки времени выполнения - используются для установки и определения атрибутов потоков. Вызовы этих подпрограмм включаются в исходный текст программы.
- Переменные окружения - используются для управления поведением параллельной программы.
Привязка к языкам
Привязка к C/C++
В программах на языке C прагмы, имена функций и переменных окружения OpenMP начинаются с omp, omp_ или OMP_. Формат директивы:
#pragma omp директива [оператор_1[, оператор_2, :]]
В OpenMP-программе используется заголовочный файл omp.h.
Привязка к языку Fortran
В программах на языке Fortran директивы компилятора, имена подпрограмм и переменных окружения начинаются с OMP или OMP_. Формат директивы компилятора:
{!|C|*}$OMP директива [оператор_1[, оператор_2, :]]
Директива начинается в первой (фиксированный формат записи текста языка Fortran 77) или произвольной (свободный формат) позиции строки. Допускается продолжение директивы в следующей строке, в этом случае действует стандартное в данной версии языка правило для обозначения строки продолжения (непробельный символ в шестой позиции для фиксированного формата записи и амперсанд для свободного формата).
Пример
#include "omp.h" #include <stdio.h> double f(double x) { return 4.0 / (1 + x * x); } main () { const long N = 100000; long i; double h, sum, x; sum = 0; h = 1.0 / N; #pragma omp parallel shared(h) { #pragma omp for private(x) reduction(+:sum) for (i = 0; i < N; i++) { x = h * (i + 0.5); sum = sum + f(x); } } printf("PI = %f\n", sum / N);
Эффективность
Эффективность приложения, распараллеленного с помощью OpenMP, зависит от баланса между выигрышем от распараллеливания и накладными расходами на организацию многопоточности, диспетчеризацию, синхронизацию и т.д.
parallel | 1.5 мкс (Intel ® Xeon 3Ггц) |
barrier | 1.0 |
schedule(static) | 1.l0 |
schedule(guided) | 6.0 |
schedule(dynamic) | 50.0 |
ordered | 0.5 |
single | 1.0 |
reduction | 2.5 |
Диспетчеризацией параллельной OpenMP-программы управляет программист с помощью оператора schedule. Поддерживаются три способа распределения работы между потоками: статический, динамический и "управляемый".
Модель программирования Intel® CilkTM Plus
Модель программирования Intel® CilkTM Plus основана на параллелизме задач. Программа пишется в семантике последовательного программирования. Фрагменты для распараллеливания расщепляются на подзадачи, связанные отношениями подчинения ("родитель"-"потомок"). Такая реализация параллелизма иногда называется "fork-join".
Программист, использующий CilkTM Plus должен думать о том, что следует распараллелить, а не как. В этом – одно из отличий от OpenMP-программирования.
Балансировкой занимается runtime-система. Балансировка выполняется методом захвата работы. Алгоритмы диспетчеризации таковы, что их эффективность, как правило, высока.
Удобные средства работы с массивами (расширенная индексная нотация – аналог сечений массивов в языке Fortran).
Удобное использование векторных расширений команд, векторизация функций.
Вычислительная работа разбивается на задачи. Каждая задача – это фрагмент большей задачи.
Программист
Определяет и описывает потенциальный параллелизм.
Планировщик
Отображает его на реально существующую конфигурацию потоков.
Задачи связаны между собой отношениями подчинения. Конфигурацию приложения во время его выполнения можно изобразить в виде направленного ациклического графа (DAG).
Граф задач в Cilk-программе является динамическим – он создается и изменяется в процессе выполнения программы.
В Intel® CilkTM Plus сохраняется семантика последовательной программы.
Программа может выполняться как в последовательном, так и в параллельном режимах.
Параллельное выполнение возможно, если это допускает целевая платформа (достаточное количество ядер).
Сериализация (выполнение программы в последовательном режиме) происходит, если степень параллелизма целевой платформы недостаточно велика.
Сериализация также происходит при использовании заголовочного файла
<cilk/cilk_stub.h>
и при компиляции с соответствующим ключом:
icc: -cilk-serialize icl: /Qcilk-serialize
В Microsoft Visual Studio сериализовать Cilk-программу можно так:
Properties --> C/C++ --> Language [Intel C++] --> Replace Intel Cilk Plus Keywords with Serial Equivalent