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 << "begin ";
// This pragma statement hints the compiler that the
// contents within the { } 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 << "hello " << std::endl;
/** Do something **/
}
#pragma omp section
{
std::cout << "world " << std::endl;
/** Do something **/
}
}
// This line will not be executed until all the
// sections defined above terminates
std::cout << "end" << std::endl;
Выходы
Этот пример дает 2 возможных выхода и зависит от операционной системы и оборудования. Результат также иллюстрирует проблему состояния гонки, которая возникла бы из такой реализации.
ВЫХОД A | ВЫХОД B |
---|---|
начать привет мир | начать мир привет |
OpenMP: параллельные разделы
В этом примере показано, как выполнять куски кода параллельно
std::cout << "begin ";
// Start of parallel sections
#pragma omp parallel sections
{
// Execute these sections in parallel
#pragma omp section
{
... do something ...
std::cout << "hello ";
}
#pragma omp section
{
... do something ...
std::cout << "world ";
}
#pragma omp section
{
... do something ...
std::cout << "forever ";
}
}
// end of parallel sections
std::cout << "end";
Выход
- начать привет мир навсегда конец
- начать мир привет навсегда конец
- начать привет навсегда
- начинать навсегда 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<int> 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::vector<int> Slave;
// 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
...