Топ контрибуторов
loading
loading
Знаете ли Вы, что

Свои вопросы для тестов можно добавлять на странице с информацией о тесте. При этом для некоторых тестов добавление вопросов закрыто

Лента обновлений
ссылка 15:04:55
Комментарий от Artale:
"2 - всё правильно, постфиксный и префиксный инкремент на...
ссылка Oct 16 19:36
Добавлен вопрос в тест HTML - Средний уровень
ссылка Oct 16 11:43
Комментарий от AlexFurm:
Ответы неправильные на данный момент указаны, правильны...
ссылка Oct 15 20:20
Комментарий от dafed000:
Я тоже решила, что 6 mod 11 = 5, но меня смущало, что д...
ссылка Oct 15 19:41
Комментарий от kate2601:
В конструктор класса Boolean можно передавать строку и ...
Статистика

Тестов: 153, вопросов: 8596. Пройдено: 442974 / 2175518.

Введение в технологию OpenMP (применение с использованием языка C++).

head tail Статья
категория
C++
дата16.03.2015
авторingwarsmith
голосов7

OpenMP предлагает простой механизм реализации параллельных вычислений в приложениях с помощью многопоточности, в которой «главный» (master) поток создает набор «подчиненных» (slave) потоков, и задача распределяется между ними. Стандарт OpenMP поддерживает языки Fortran, C/C++ (Fortran далее не рассматривается) и сформулирован как API для написания переносимых многопоточных приложений на многопроцессорных системах с общей памятью (SPMD – Single Program Multiple Data). Переносимость связана с моделью программирования OpenMP, предоставляющей независимый от платформы набор директив (прагм) компилятора, вызовов функций и переменных среды, которые явно показывают компилятору, где и как использовать параллелизм в приложении. Т.е. программист не обременяется дополнительными сложностями, связанными с созданием, синхронизацией, балансировкой нагрузки и уничтожением потоков.

OpenMP можно рассматривать как высокоуровневую надстройку над Pthreads – POSIX-интерфейсом для организации потоков. В самом Pthreads – слишком низкий уровень программирования, нет поддержки параллелизма по данным, а сам механизм потоков изначально разрабатывался не для целей организации параллелизма. В OpenMP используется терминология и модель программирования, близкая к Pthreads, например, динамически порождаемые потоки, общие и разделяемые данные и т.д.

Технология OpenMP нацелена на то, чтобы пользователь имел один вариант программы для параллельного и последовательного выполнения. Однако возможно создавать программы, которые работают корректно только в параллельном режиме или дают в последовательном режиме другой результат. Кроме того, из-за накопления ошибок округления результат выполнения вычислений с использованием различного количества потоков может в некоторых случаях различаться.

Для использования механизмов OpenMP нужно скомпилировать программу компилятором, поддерживающим OpenMP, с указанием соответствующего ключа (например, в gcc используется ключ –fopenmp).

Потенциал OpenMP реализуется полностью при использовании для поточной обработки самых трудоемких циклов («горячих точек»). Для параллельного выполнения цикла for в OpenMP требуется единственная прагма, вставленная непосредственно перед циклом:

#pragma omp parallel for

Директивы OpenMP в программах на языках C и C++ оформляются указаниями препроцессору, начинающимися с #pragma omp. Формат директивы следующий:

#pragma omp directive-name [option[[,] option]…]

Объектом большинства директив является оператор или блок, перед которым расположена директива в исходном тексте программы – т.н. ассоциированный оператор/блок. Ассоциированный блок должен иметь одну точку входа в начале и одну точку входа в конце. Это означает, что не разрешаются переходы изнутри такого блока наружу или снаружи внутрь – инструкция goto или break должна приводить к переходу только внутри блока, и исключения должны перехватываться внутри блока. Это не касается вызова функции exit, которая завершает всё приложение.

Чтобы задействовать функции библиотеки OpenMP, нужно включить в программу заголовочный файл omp.h. При этом функции назначения параметров имеют приоритет над соответствующими переменными окружения. Все функции, используемые в OpenMP, имеют префикс omp_.

Также в OpenMP накладываются следующие ограничения на параллельно выполняемые циклы:

  1. Операция сравнения должна иметь форму loop_vaiable <, <=, > или >= loop_invariant_integer.
  2. Третье выражение (часть, содержащая инкремент переменной цикла for) должна быть либо целым сложением, либо целым вычитанием значения, не зависящего от итерации цикла.
  3. Если операцией является < или <=, то переменная цикла должна инкрементироваться при каждой итерации и декрементироваться в ином случае.

Даже если цикл соответствует всем указанным условиям, и компилятор организовал поточную обработку данного цикла, он все равно может работать неправильно, если в нем имеются зависимости по данным, которые компилятор проигнорировал вследствие наличия OpenMP-прагм. Теория зависимости по данным говорит, что инструкция S2 зависит по данным от инструкции S1, если выполняются два условия:

  1. Существует такой путь выполнения, при котором обе инструкции S1 и S2 обращаются к одному и тому же месту в памяти L.
  2. Выполнение инструкции S1, которая обращается к памяти L, происходит перед выполнением инструкции S2, которая также обращается к памяти L.

Для того, чтобы инструкция S2 зависела от S1, необходимо, чтобы при выполнении S1 производилась запись в область памяти L, которая позднее при выполнении S2 будет читаться – это есть зависимость по ходу выполнения. Существуют также иные типы зависимостей. Когда обе инструкции пишут в одну и ту же область памяти L, это называется выходной зависимостью, а когда чтение происходит до записи – антизависимостью.

Результатом выходных зависимостей является гонка данных, когда несколько программных потоков пытаются обновить одну и ту же область памяти (или переменную). Состояние гонок иногда бывает не просто выявить. При использовании всех средств синхронизации, имеющихся в Windows API или Pthreads, разработчики с большей вероятностью могут избежать этих проблем, так как данные с самого начала будут управляться с учетом конкуренции программных потоков и условий гонок. Однако в случае ОреnМР условия гонок легко не заметить.

В приведенном далее примере показанный цикл приведет к нежелательным результатам, т.к. несколько программных потоков обновляют переменную x:



#pragma omp parallel for
for (int i = 0; i < 80; ++i)
{
        x = 23*i + pow((double) i, 2.0);
        x += 35;
        if (x < 1000)
	std::cout << x << std::endl;
}

Здесь одним из выходов будет добавить опцию private(x) к прагме parallel for:

#pragma omp parallel for private(x)
for (int i = 0; i < 80; ++i)
{
        x = 23*i + pow((double) i, 2.0);
        x += 35;
        if (x < 1000)
	std::cout << x << std::endl;
}

При написании многопоточных программ становится исключительно важным понимание того, какие данные являются общими (принадлежащими всем потокам), а какие остаются приватными (имеющими свои копии для каждого потока). ОреnМР делает эту разницу очевидной для программиста при помощи целого набора предложений, таких как shared, private и default, причем это тот аспект, который можно настроить вручную. При использовании ОреnМР именно разработчик указывает компилятору, какие области памяти должны быть для потоков общими, а какие должны оставаться приватными. Когда память помечается как общая, все потоки обращаются к одному и тому же месту в памяти. Однако когда память помечается как приватная, то для каждого потока делается копия данной переменной, чтобы поток мог обращаться к ней частным образом. Когда поток завершается, эти приватные копии становятся неопределенными.

По умолчанию все переменные в параллельной области являются общими за исключением трех случаев:

- в циклах parallel for индекс цикла приватен;

- переменные, локальные для параллельной области, приватны;

- любые переменные, указанные в опциях pivate, firtsprivate, lastprivate и reduction, приватны.

Для того, чтобы задать глобальные переменные, которые должны быть приватными для каждого потока, нужно использовать прагму threadprivate (#pragma omp threadprivate(x)).

Следующий цикл работает некорректно, поскольку переменная x общая. Она должна быть приватной, т.к. проблема в выходной циклической зависимости по переменной x и имеет место состояние гонок – в то время, как один поток читает переменную, другой поток может писать в неё:

#pragma omp parallel for
for (int i = 0; i < 500; ++i)
{ 
        x = array[i];
        array[i] = doWork(x);
}

Проблему можно устранить одним из следующих двух способов, причем в них обоих переменная x объявляется как приватная:

#pragma omp parallel for private(x) // переменная x объявлена ранее
for (int i = 0; i < 500; ++i)
{
        x = array[i];
        array[i] = doWork(x);
}

либо

#pragma omp parallel for
for (int i = 0; i < 500; ++i)
{
        int x = array[i];   // переменная x локальная в цикле (приватная)
        array[i] = doWork(x);
}

Чтобы при использовании ОреnМР обеспечить параллельное выполнение цикла, всегда следует тщательно изучить все ссылки на ячейки памяти, включая ссылки из вызываемых функций.

Использованная литература:

1. Антонов А.С. Параллельное программирование с использованием технологии OpenMP: Учебное пособие. – М.: Изд-во МГУ, 2009. – 77 с.

2. Эхтер Ш., Робертс Дж. Многоядерное программирование. — СПб.: Питер, 2010. — 316 с: ил. — (Серия «Библиотека программиста»).

Если Вам понравилась статья, проголосуйте за нее

Голосов: 7  loading...
astalabaster   ref   phantom_r   WitcherEdge   Filip   amironsoft   gvvynplaine