C++
Concurrencia con OpenMP
Buscar..
Introducción
Este tema cubre los conceptos básicos de la concurrencia en C ++ utilizando OpenMP. OpenMP se documenta con más detalle en la etiqueta OpenMP .
Paralelismo o concurrencia implica la ejecución de código al mismo tiempo.
Observaciones
OpenMP no requiere encabezados ni bibliotecas especiales, ya que es una característica de compilador integrada. Sin embargo, si usa alguna de las funciones de la API de OpenMP como omp_get_thread_num()
, deberá incluir omp.h
su biblioteca.
Las instrucciones pragma
OpenMP se ignoran cuando la opción OpenMP no está habilitada durante la compilación. Es posible que desee consultar la opción del compilador en el manual de su compilador.
- GCC utiliza
-fopenmp
- Clang usa
-fopenmp
- MSVC usa
/openmp
OpenMP: Secciones paralelas
Este ejemplo ilustra los conceptos básicos de la ejecución de secciones de código en paralelo.
Como OpenMP es una característica de compilador incorporada, funciona en cualquier compilador compatible sin incluir bibliotecas. Si lo desea, puede incluir omp.h
si desea usar cualquiera de las funciones de la API openMP.
Código de muestra
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;
Salidas
Este ejemplo produce 2 salidas posibles y depende del sistema operativo y el hardware. La salida también ilustra un problema de condición de carrera que se produciría a partir de dicha implementación.
SALIDA A | SALIDA B |
---|---|
empieza hola fin mundo | comienza el mundo hola fin |
OpenMP: Secciones paralelas
Este ejemplo muestra cómo ejecutar trozos de código en paralelo
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";
Salida
- comienza hola mundo para siempre termina
- comienza el mundo hola para siempre termina
- comience hola para siempre fin del mundo
- comienza por siempre hola fin del mundo
Como el orden de ejecución no está garantizado, puede observar cualquiera de los resultados anteriores.
OpenMP: Parallel For Loop
Este ejemplo muestra cómo dividir un bucle en partes iguales y ejecutarlas en paralelo.
// 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
...
* Tenga mucho cuidado de no modificar el tamaño del vector utilizado en paralelo para los bucles, ya que los índices de rango asignado no se actualizan automáticamente .
OpenMP: Recopilación paralela / Reducción
Este ejemplo ilustra un concepto para realizar una reducción o recopilación utilizando std::vector
y OpenMP.
Suponemos que tenemos un escenario en el que queremos que varios subprocesos nos ayuden a generar un montón de cosas, int
se usa aquí para simplificar y se puede reemplazar con otros tipos de datos.
Esto es particularmente útil cuando necesita combinar resultados de esclavos para evitar fallas de segmentos o violaciones de acceso a la memoria y no desea usar bibliotecas o bibliotecas personalizadas de contenedores de sincronización.
// 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
...