Доброго времени суток, друзья! Как вы могли заметить, я иногда балуюсь с параллельным программированием посредством OpenMP. На данный момент готова заметка о том, как установить и настроить omp в Clion. А из реализаций есть параллельное умножение матриц.
Я посчитал, что самое время собрать небольшую шпаргалку по наиболее часто используемым директивам и их параметрам, чтобы можно было сюда заглянуть и освежить их в памяти. Постараюсь на каждую директиву добавить по небольшому, чисто символическому синтаксическому примеру.
Не секрет, что OpenMP доступен на языках C/C++ и Fortran(даже слышал, что и на Java есть), но я буду писать примеры только для C/C++, уж не сердитесь, теория для них все равно одинаковая. Предлагаю ни секунды не терять и приступать к делу.
Общий синтаксис вызова директив OpenMP
Любые дополнительные директивы вызываются с помощью стандартной директивы #pragma, и OpenMP не исключение. Следовательно, для того, чтобы обратиться к директиве нужно написать #pragma omp, и назвать искомую директиву. Справедлива следующая конструкция вызова.
OpenMP
#pragma omp директива [опция1, опция2, . ]
Где «директива» — имя директивы, а опции являются необязательным для вызова, их у разных директив может быть разное количество, о них я тоже вкратце расскажу.
Директива parallel
Самая основная директива, служит для создания нитей(threads). Блок, который следует за директивой будет выполнен параллельно. Количество вновь созданных нитей можно установить с помощью вызова функции omp_set_num_threads() передав ей целое число соответствующее желаемому количеству. Узнать номер нити в коде можно вызвав функцию omp_get_thread_num().
Важно! Если написать вложенную директиву parallel, то каждая из созданных нитей, встретив директиву, создаст еще такое же количество дополнительных нитей, будьте внимательны.
#pragma omp parallel [опция1, опция2, . ] < //блок >
Опции директивы parallel
shared(переменные) — в скобочках можно явно перечислить список переменных, которые будут общими для всех нитей, хотя по умолчанию все переменные, объявленные до блока таковыми являются.
private(переменные) — в скобочках можно указать список переменных, которые будут частными для каждой нити.
if(условие) — проверка условия, если true, то выполняется директива, если false, то код исполняется в прежнем режиме, без создания нитей. Может быть только одна такая опция на директиву.
Пример использования
omp_set_num_threads(4); //Установить количество нитей #pragma omp parallel shared(num) //В этот момент создается 4 нити, переменная num у них будет общей < cout
omp_set_num_threads(4); //Установить количество нитей #pragma omp parallel //В этот момент создается 4 нити < //А в этот момент каждая из 4 нитей создаст еще 4, // итого 16 нитей буду выполнять следующий блок #pragma omp parallel < //блок >>
Директива for
Используется для явного распараллеливания следующего цикла for, при этом каждая нить начнет со своего индекса. Если не указывать директиву, то цикл будет пройден каждой нитью полностью от начала и до конца.
Вводная — Параллельные(многопоточные) вычисления в OpenMP и C++17.
#pragma omp for [опция1, опция2] for(int i = 0; i < 100; i++)
nowait — опция позволяет нитям избежать барьерной синхронизации в конце цикла, которая выполняется по умолчанию.
Пример использования
omp_set_num_threads(4); #pragma omp for //в этот момент каждая из нитей возьмет себе по индексу и начнет исполнение цикла for(int i = 0; i < 8; i++) < //блок >
Нужно очень внимательно относиться к распараллеливанию циклов в программе, точно знать, что алгоритм не пострадает от того, что не все нити полностью пройдут по циклу.
Директива single
Служит для того, чтобы выполнить некий блок в параллельной области только один раз. Часто используется для выводов, чтобы только одна нить печатала информацию.
#pragma omp single
Пример использования
omp_set_num_threads(4); #pragma omp for for(int i = 0; i
При этом какая именно нить выполнит участок кода, заранее мы никак не узнаем.
Директива master
Аналогична директиве single, но мы точно знаем, что блок выполнится мастер-нитью. Мастером является та нить, которая породила все остальные, ее порядковый номер равен 0.
Директива critical
Похожа на две предыдущие. Разница заключается в том, что блок после директивы critical выполнится всеми нитями, но по очереди. Если блок уже исполняется любой нитью, то все остальные блокируются на входе и ждут завершения. После завершения работы, случайным образом из списка заблокированных выбирается новая. Таким образом в один момент блок будет исполняться только одной нитью.
Пример использования
omp_set_num_threads(4); #pragma omp parallel < int n = 1; #pragma omp critical //Тут образуется очередь на вход < n++; cout >
Директива barrier
Классический метод барьерной синхронизации, нити будут блокироваться на этой директиве до тех пор, пока не дождутся всех. Как только все нити достигают этой точки, работа продолжается.
Пример использования
omp_set_num_threads(4); #pragma omp parallel < int n = 1000; for(int i = 0; i < n; i++) < someFunc(); >//Исполнение кода заблокируется до тех пор, пока все нити не пройдут цикл #pragma omp barrier someFunc2(); >
Заключение
Это далеко не самый полный список, признаю, сейчас я записал сюда только самые основные директивы и опции, которые использую, использовал и буду использовать. Поэтому вполне вероятно, что список, описания и примеры будут пополняться. Но на сегодня у меня все, спасибо за внимание!
Источник: mindhalls.ru
C++
Параллельность с OpenMP
В этом разделе рассматриваются основы параллелизма в C ++ с использованием OpenMP. OpenMP документируется более подробно в теге OpenMP .
Параллелизм или параллелизм подразумевают выполнение кода одновременно.
замечания
OpenMP не требует каких-либо специальных заголовков или библиотек, поскольку это встроенная функция компилятора. Однако, если вы используете какие-либо функции OpenMP API, такие как omp_get_thread_num() , вам нужно будет omp.h и его библиотеку.
Операторы pragma OpenMP игнорируются, когда опция OpenMP не включена во время компиляции. Вы можете обратиться к опции компилятора в руководстве вашего компилятора.
- GCC использует -fopenmp
- Кланг использует -fopenmp
- MSVC использует /openmp
OpenMP: параллельные разделы
Этот пример иллюстрирует основы выполнения разделов кода параллельно.
Поскольку OpenMP является встроенной функцией компилятора, он работает с любыми поддерживаемыми компиляторами без включения каких-либо библиотек. Возможно, вы захотите включить omp.h если вы хотите использовать какие-либо функции OpenMP API.
Образец кода
std::cout are to be executed in as // parallel sections using openMP, the compiler will // generate this chunk of code for parallel execution #pragma omp parallel sections < // This pragma statement hints the compiler that // this is a section that can be executed in parallel // with other section, a single section will be executed // by a single thread. // Note that it is «section» as opposed to «sections» above #pragma omp section < std::cout #pragma omp section < std::cout > // This line will not be executed until all the // sections defined above terminates std::cout
Выходы
Этот пример дает 2 возможных выхода и зависит от операционной системы и оборудования. Результат также иллюстрирует проблему состояния гонки, которая возникла бы из такой реализации.
начать привет мир | начать мир привет |
OpenMP: параллельные разделы
В этом примере показано, как выполнять куски кода параллельно
std::cout #pragma omp section < . do something . std::cout #pragma omp section < . do something . std::cout > // end of parallel sections std::cout
Выход
- начать привет мир навсегда конец
- начать мир привет навсегда конец
- начать привет навсегда
- начинать навсегда hello world end
Поскольку порядок выполнения не гарантируется, вы можете наблюдать за любым из вышеперечисленных результатов.
OpenMP: Параллельный для цикла
В этом примере показано, как разделить цикл на равные части и выполнить их параллельно.
// Splits element vector into element.size() / Thread Qty // and allocate that range for each thread. #pragma omp parallel for for (size_t i = 0; i < element.size(); ++i) element[i] = . // Example Allocation (100 element per thread) // Thread 1 : 0 ~ 99 // Thread 2 : 100 ~ 199 // Thread 2 : 200 ~ 299 // . // Continue process // Only when all threads completed their allocated // loop job .
* Пожалуйста, проявляйте особую осторожность, чтобы не изменять размер вектора, который используется параллельно для циклов, поскольку выделенные индексы диапазона не обновляются автоматически .
OpenMP: параллельная сборка / сокращение
Этот пример иллюстрирует концепцию выполнения сокращения или сбора с использованием std::vector и OpenMP.
Предположим, у нас есть сценарий, когда мы хотим, чтобы несколько потоков помогли нам создать кучу материала, int используется здесь для простоты и может быть заменен другими типами данных.
Это особенно полезно, когда вам нужно объединить результаты с ведомыми устройствами, чтобы избежать сбоев в сегментировании или нарушения доступа к памяти, и не хотите использовать библиотеки или пользовательские библиотеки контейнеров синхронизации.
// The Master vector // We want a vector of results gathered from slave threads std::vector Master; // Hint the compiler to parallelize this < >of code // with all available threads (usually the same as logical processor qty) #pragma omp parallel < // In this area, you can write any code you want for each // slave thread, in this case a vector to hold each of their results // We don’t have to worry about how many threads were spawn or if we need // to repeat this declaration or not. std::vectorSlave; // Tell the compiler to use all threads allocated for this parallel region // to perform this loop in parts. Actual load appx = 1000000 / Thread Qty // The nowait keyword tells the compiler that the slave threads don’t // have to wait for all other slaves to finish this for loop job #pragma omp for nowait for (size_t i = 0; i < 1000000; ++i < /* Do something */ . Slave.push_back(. ); >// Slaves that finished their part of the job // will perform this thread by thread one at a time // critical section ensures that only 0 or 1 thread performs // the < >at any time #pragma omp critical < // Merge slave into master // use move iterators instead, avoid copy unless // you want to use it for something else after this section Master.insert(Master.end(), std::make_move_iterator(Slave.begin()), std::make_move_iterator(Slave.end())); >> // Have fun with Master vector .
Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow
- Начало работы с C ++
- C несовместимости
- Const Correctness
- constexpr
- decltype
- Loops
- Perfect Forwarding
- RAII: Приобретение ресурсов является инициализацией
- RTTI: информация о времени выполнения
- SFINAE (ошибка замены не является ошибкой)
- static_assert
- std :: function: для обертывания любого элемента, вызываемого
- std :: set и std :: multiset
- авто
- Арифметика с плавающей точкой
- Арифметическое метапрограммирование
- Атомные типы
- Атрибуты
- Базовый ввод / вывод в c ++
- Без названия
- Бит-манипуляция
- Более неопределенное поведение в C ++
- Виртуальные функции участника
- Возвращение нескольких значений из функции
- Встроенные переменные
- Встроенные функции
- Вывод типа
- Вызываемые объекты
- вычет типа
- Генерация случайных чисел
- Дата и время использования заголовок
- Значение и справочная семантика
- Идиома Pimpl
- Интернационализация в C ++
- Исключения
- Использование std :: unordered_map
- Использование декларации
- итераторы
- итерация
- Категории ценностей
- Классы / Структуры
- ключевое слово const
- Ключевое слово Friend
- ключевое слово mutable
- Ключевые слова
- Ключевые слова с переменной переменной
- Ковариантность типа возврата
- Компиляция и строительство
- Константные функции члена класса
- Контейнеры C ++
- Копирование Elision
- Копирование и назначение
- литералы
- Любопытно повторяющийся шаблон шаблона (CRTP)
- Лямбда
- Макет типов объектов
- Массивы
- Метапрограммирование
- Методы рефакторинга
- Многопоточность
- Модель памяти C ++ 11
- Мьютексы
- Неопределенное поведение
- Неопределенное поведение
- Области применения
- Обратный тип возврата
- Общие ошибки компиляции / компоновщика (GCC)
- Одно правило определения (ODR)
- Операторы бит
- оптимизация
- Оптимизация в C ++
- Основные ключевые слова
- Пакеты параметров
- Параллельность с OpenMP
- Перегрузка оператора
- Перегрузка функции
- Перегрузка функциональных шаблонов
- Переместить семантику
- перечисление
- Поведение, определяемое реализацией
- Поиск зависимого имени аргумента
- Полиморфизм
- Пользовательские литералы
- Поля бит
- Потоки C ++
- Потолочные манипуляторы
- Правило три, пять и ноль
- препроцессор
- Примеры клиентского сервера
- приоритет оператора
- Пространства имен
- профилирование
- Разрешение перегрузки
- Реализация шаблона проектирования в C ++
- Регулярные выражения
- Рекомендации
- Рекурсивный Мьютекс
- Рекурсия в C ++
- Сгибание выражений
- семафор
- Системы сборки
- Сортировка
- Союзы
- Специальные функции участников
- Спецификаторы класса хранения
- Сравнение сборок с классическими примерами C ++, разрешенными с помощью C ++ vs C ++ 11 vs C ++ 14 vs C ++ 17
- Средства и методы отладки и отладки C ++
- станд :: forward_list
- станд :: integer_sequence
- станд :: iomanip
- станд :: вариант
- станд :: вектор
- станд :: Карта
- станд :: любой
- станд :: массив
- станд :: опционально
- станд :: пара
- станд :: строка
- Стандарт ISO C ++
- Стандартные библиотечные алгоритмы
- СТД :: атомарных
- Структуры данных в C ++
- Структуры синхронизации потоков
- Тестирование модулей в C ++
- Тип Erasure
- Тип Ключевые слова
- Типед и псевдонимы типов
- Типовые черты
- указатели
- Указатели для участников
- Умные указатели
- Управление памятью
- Управление потоком
- Управление ресурсами
- Файловый ввод-вывод
- Файлы заголовков
- Функции нестатического пользователя
- Функция C ++ «вызов по значению» против «вызов по ссылке»
- Фьючерсы и обещания
- Характеристики связи
- центровка
- Цифровые разделители
- Шаблон дизайна Singleton
- Шаблоны
- Шаблоны выписки
- Этот указатель
- Явные преобразования типов
Источник: learntutorials.net
Распараллеливание циклов в openmp
OpenMP – библиотека для языков программирования Fortran, C, C++, позволяющая запускать параллельно участки кода на многопроцессорных системах или процессорах с HyperThreading. Встроена в компилятор gcc начиная с версии 4.2. Разрабатывается при активном участии Intel, за что им большое спасибо.
Несколько правил по активации данного режима:
Распараллеливание можно ввести на участок программы, то есть определённый блок будет выполняться несколько раз (задаётся пользователем) или для циклов for. На первом случае мы останавливаться не будем, об этом можно прочитать в презентации OpenMP_talk, затронем лишь распараллеливание циклов.
В данном примере цикл for будет выполнятся в два потока, при этом нельзя заранее определить в какой последовательности значений переменной i будет происходит выполнение тела цикла. Таким образом, необходимо избавится от зависимости от предыдущего шага.
Например, если у вас есть такой цикл:
То его нужно преобразовать к виду:
Очень часто встречается такой случай:
Переменная ave накапливает значения и зависит от итерации. Если вы запустите программу в параллельном режиме, то можете получать различные ответы. Как я понимаю, дело в том, что чтобы прибавить к переменной ave значение A[i], из памяти в регистр процессора копируется значение ave, суммируется, а затем новое значение записывается в участок памяти этой переменной. В промежутке между считыванием и записью другой процесс может записать новое суммированное значение, которое потом будет потеряно и не учтено.
Учёт такого случая называется reduction и объявляется следующим образом для переменных:
После чего для каждой переменной list создаётся локальная копия и инициализируется согласно операции op – +, *, – (странно, а / нет) 0, 1, 0 соответственно. После вычисления эта локальная копия комбинируется с глобальной переменной.
Таким образом, корректная запись предыдущего примера выглядит следующим образом:
Если вы хотите использовать генератор случайных чисел внутри цикла, который идёт на распараллеливание, то нужно быть очень внимательным, так как если для этого используется задаваемая последовательность псевдослучайных чисел, то они могут перекрываться между собой. Подробно об этом написано в OpenMP_talk.
Итак, вы готовы к программированию распараллеливания циклов. Основные правила – избавиться от зависимостей от итерации, желательно использовать внутри циклов только локальные переменные цикла, внимательно следить за присваиваниями глобальным переменным (выделять эти случаи как reduction). Обязательно сравните результаты до и после.