c++ - example - openmp ubuntu
¿Puedo usar OpenMP de forma segura con C++ 11? (4)
En realidad, estoy interesado en la informática de alto rendimiento, pero OpenMP (actualmente) no cumple mi propósito lo suficientemente bien: no es lo suficientemente flexible (mi algoritmo no está basado en bucle)
Tal vez estás realmente buscando TBB ? Proporciona soporte para el paralelismo basado en bucles y tareas, así como una variedad de estructuras de datos paralelas, en C ++ estándar, y es portátil y de código abierto.
(Descargo de responsabilidad: trabajo para Intel que están muy involucrados con TBB, aunque en realidad no trabajo en TBB, pero sí en OpenMP :-); ¡Ciertamente no estoy hablando por Intel!).
El estándar OpenMP solo considera C ++ 98 (ISO / IEC 14882: 1998). Esto significa que no hay un estándar que soporte el uso de OpenMP bajo C ++ 03 o incluso C ++ 11. Por lo tanto, cualquier programa que use C ++> 98 y OpenMP funciona fuera de los estándares, lo que implica que incluso si funciona bajo ciertas condiciones, es poco probable que sea portátil, pero definitivamente nunca garantizado.
La situación es aún peor con C ++ 11 con su propio soporte multithreading, que muy probablemente entrará en conflicto con OpenMP para ciertas implementaciones.
Entonces, ¿qué tan seguro es usar OpenMP con C ++ 03 y C ++ 11?
¿Se puede usar con seguridad C ++ 11 multi-threading y OpenMP en el mismo programa pero sin intercalarlos (es decir, ninguna declaración OpenMP en ningún código pasado a las funciones simultáneas de C ++ 11 y ninguna concurrencia de C ++ 11 en los hilos)? generado por OpenMP)?
Estoy particularmente interesado en la situación en la que primero invoco un código usando OpenMP y luego otro código usando la concurrencia de C ++ 11 en las mismas estructuras de datos.
Al igual que Jim Cownie, también soy un empleado de Intel. Estoy de acuerdo con él en que Intel Threading Building Blocks (Intel TBB) podría ser una buena opción, ya que tiene paralelismo a nivel de bucle como OpenMP pero también otros algoritmos paralelos, contenedores simultáneos y funciones de nivel más bajo también. Y TBB intenta mantenerse al día con el estándar actual de C ++.
Y para clarificar para Walter, Intel TBB incluye un algoritmo parallel_reduce así como soporte de alto nivel para atómicos y mutexes.
Puede encontrar la Guía del usuario de Intel® Threading Building Block en http://software.intel.com/sites/products/documentation/doclib/tbb_sa/help/tbb_userguide/title.htm La Guía del usuario ofrece una descripción general de las funciones en el biblioteca.
OpenMP es a menudo (no tengo excepciones) implementado sobre Pthreads, por lo que puede razonar sobre algunas de las preguntas de interoperabilidad al pensar en cómo la concurrencia C ++ 11 interopera con el código Pthread.
No sé si la sobresuscripción debido al uso de múltiples modelos de subprocesamiento es un problema para usted, pero este es definitivamente un problema para OpenMP. Hay una proposal para abordar esto en OpenMP 5. Hasta entonces, la forma de resolver esto es la implementación definida. Son pesados martillos, pero puede usar OMP_WAIT_POLICY
(OpenMP 4.5+), KMP_BLOCKTIME
(Intel y LLVM) y GOMP_SPINCOUNT
(GCC) para solucionar esto. Estoy seguro de que otras implementaciones tienen algo similar.
Un problema en el que la interoperabilidad es una preocupación real es el modelo de memoria, es decir, cómo se comportan las operaciones atómicas. Esto no está definido actualmente, pero aún puede razonar al respecto. Por ejemplo, si usa C ++ 11 atómico con OpenMP paralelismo, debería estar bien, pero usted es responsable de usar Cómica 11 atómica correctamente desde OpenMP threads.
La combinación de atomics OpenMP y C ++ 11 atomics es una mala idea. Nosotros (el grupo de trabajo del comité de lenguaje OpenMP encargado de examinar el soporte de lenguaje base de OpenMP 5) actualmente estamos tratando de resolver esto. Personalmente, creo que C ++ 11 atómica es mejor que OpenMP atomics en todos los sentidos, así que mi recomendación es que uses C ++ 11 (o C11, o __atomic
) para tus átomos y dejes #pragma omp atomic
para los programadores de Fortran.
A continuación se muestra un código de ejemplo que utiliza átomos C ++ 11 con hilos OpenMP. Funciona como está diseñado en todos los lugares donde lo he probado.
Revelación completa: como Jim y Mike, trabajo para Intel :-)
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#include <iostream>
#include <iomanip>
#include <atomic>
#include <chrono>
#ifdef _OPENMP
# include <omp.h>
#else
# error No OpenMP support!
#endif
#ifdef SEQUENTIAL_CONSISTENCY
auto load_model = std::memory_order_seq_cst;
auto store_model = std::memory_order_seq_cst;
#else
auto load_model = std::memory_order_acquire;
auto store_model = std::memory_order_release;
#endif
int main(int argc, char * argv[])
{
int nt = omp_get_max_threads();
#if 1
if (nt != 2) omp_set_num_threads(2);
#else
if (nt < 2) omp_set_num_threads(2);
if (nt % 2 != 0) omp_set_num_threads(nt-1);
#endif
int iterations = (argc>1) ? atoi(argv[1]) : 1000000;
std::cout << "thread ping-pong benchmark/n";
std::cout << "num threads = " << omp_get_max_threads() << "/n";
std::cout << "iterations = " << iterations << "/n";
#ifdef SEQUENTIAL_CONSISTENCY
std::cout << "memory model = " << "seq_cst";
#else
std::cout << "memory model = " << "acq-rel";
#endif
std::cout << std::endl;
std::atomic<int> left_ready = {-1};
std::atomic<int> right_ready = {-1};
int left_payload = 0;
int right_payload = 0;
#pragma omp parallel
{
int me = omp_get_thread_num();
/// 0=left 1=right
bool parity = (me % 2 == 0);
int junk = 0;
/// START TIME
#pragma omp barrier
std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();
for (int i=0; i<iterations; ++i) {
if (parity) {
/// send to left
left_payload = i;
left_ready.store(i, store_model);
/// recv from right
while (i != right_ready.load(load_model));
//std::cout << i << ": left received " << right_payload << std::endl;
junk += right_payload;
} else {
/// recv from left
while (i != left_ready.load(load_model));
//std::cout << i << ": right received " << left_payload << std::endl;
junk += left_payload;
///send to right
right_payload = i;
right_ready.store(i, store_model);
}
}
/// STOP TIME
#pragma omp barrier
std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
/// PRINT TIME
std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
#pragma omp critical
{
std::cout << "total time elapsed = " << dt.count() << "/n";
std::cout << "time per iteration = " << dt.count()/iterations << "/n";
std::cout << junk << std::endl;
}
}
return 0;
}
#else // C++11
#error You need C++11 for this test!
#endif // C++11
Walter, creo que no solo te conté el estado actual de las cosas en esa otra discusión , sino que también te proporcioné información directamente de la fuente (es decir, de mi colega que es parte del comité de lenguaje de OpenMP).
OpenMP fue diseñado como una adición liviana de datos paralelos a FORTRAN y C, luego extendida a modismos C ++ (por ejemplo, bucles paralelos sobre iteradores de acceso aleatorio) y al paralelismo de tareas con la introducción de tareas explícitas. Está destinado a ser tan portátil en tantas plataformas como sea posible y para proporcionar esencialmente la misma funcionalidad en los tres idiomas. Su modelo de ejecución es bastante simple: una aplicación de subproceso único bifurca equipos de subprocesos en regiones paralelas, ejecuta algunas tareas de cómputo dentro y luego vuelve a unir a los equipos en la ejecución en serie. Cada hilo de un equipo paralelo más tarde puede bifurcar a su propio equipo si el paralelismo anidado está habilitado.
Dado que el uso principal de OpenMP está en Computación de Alto Rendimiento (después de todo, su modelo de directiva y ejecución fue tomado de High Performance Fortran), el objetivo principal de cualquier implementación de OpenMP es la eficiencia y no la interoperabilidad con otros paradigmas de enhebrado. En algunas plataformas, la implementación eficiente solo podría lograrse si el tiempo de ejecución de OpenMP es el único que controla los hilos del proceso. También hay ciertos aspectos de OpenMP que podrían no funcionar bien con otras construcciones de subprocesos, por ejemplo, el límite en el número de subprocesos establecidos por OMP_THREAD_LIMIT
al bifurcar dos o más regiones paralelas concurrentes.
Dado que el estándar OpenMP en sí mismo no prohíbe estrictamente el uso de otros paradigmas de enhebrado, pero tampoco estandariza la interoperabilidad con tal, el soporte de dicha funcionalidad depende de los implementadores. Esto significa que algunas implementaciones pueden proporcionar una ejecución concurrente segura de regiones OpenMP de nivel superior, y otras no. Los implementadores x86 se comprometen a apoyarlo, puede ser porque la mayoría de ellos también son proponentes de otros modelos de ejecución (por ejemplo, Intel con Cilk y TBB, GCC con C ++ 11, etc.) y x86 suele considerarse una plataforma "experimental" ( otros vendedores suelen ser mucho más conservadores).
OpenMP 4.0 tampoco va más allá de ISO / IEC 14882: 1998 para las características de C ++ que emplea (el borrador de SC12 está here ). El estándar ahora incluye cosas como la afinidad de hilos portátiles; esto definitivamente no funciona bien con otros paradigmas de enhebrado, que podrían proporcionar sus propios mecanismos de encuadernación que chocan con los de OpenMP. Una vez más, el lenguaje OpenMP está dirigido a HPC (aplicaciones científicas y de ingeniería paralelas de datos y tareas). Los constructos de C ++ 11 están destinados a aplicaciones informáticas de uso general. Si desea cosas concurrentes de C ++ 11 concurrentes, entonces use C ++ 11 solamente, o si realmente necesita mezclarlo con OpenMP, luego, continúe con el subconjunto de características de lenguaje de C ++ 98 si desea mantenerse portátil.
Estoy particularmente interesado en la situación en la que primero invoco un código usando OpenMP y luego otro código usando la concurrencia de C ++ 11 en las mismas estructuras de datos.
No hay razones obvias para que lo que desea no sea posible, pero depende de su compilador OpenMP y del tiempo de ejecución. Existen bibliotecas gratuitas y comerciales que usan OpenMP para ejecución paralela (por ejemplo, MKL), pero siempre hay advertencias (aunque a veces ocultas en sus manuales de usuario) de la posible incompatibilidad con el código multiproceso que proporciona información sobre qué y cuándo es posible. Como siempre, esto está fuera del alcance del estándar OpenMP y, por lo tanto, YMMV.