2011-06-28 14 views
11

Estoy experimentando con OpenMP. Escribí un código para verificar su rendimiento. En una CPU Intel de 4 núcleos única con Kubuntu 11.04, el siguiente programa compilado con OpenMP es alrededor de 20 veces más lento que el programa compilado sin OpenMP. ¿Por qué?¿Por qué la versión OpenMP es más lenta?

He compilado por g ++ -g -O2 -funroll-bucles -march -fomit-frame-pointer = -fopenmp nativa

#include <math.h> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    long double i=0; 
    long double k=0.7; 

    #pragma omp parallel for reduction(+:i) 
    for(int t=1; t<300000000; t++){  
    for(int n=1; n<16; n++){ 
     i=i+pow(k,n); 
    } 
    } 

    cout << i<<"\t"; 
    return 0; 
} 
+3

** Nunca he usado openMP ** pero me parece que la sobrecarga de crear múltiples hilos y sincronizar el acceso a los datos compartidos a través de esos hilos supera (mucho) la ganancia de distribuir el procesamiento en 4 núcleos diferentes. –

+0

Pero 20 veces parece demasiado extremo. –

+0

Si está tratando de verificar el rendimiento de OpenMP, sería una buena idea utilizar un código en paralelo mejor diseñado. –

Respuesta

15

El problema es que la variable k es considerado como una variable compartida , por lo que debe sincronizarse entre los hilos. Una posible solución para evitar esto es:

#include <math.h> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    long double i=0; 

#pragma omp parallel for reduction(+:i) 
    for(int t=1; t<30000000; t++){  
    long double k=0.7; 
    for(int n=1; n<16; n++){ 
     i=i+pow(k,n); 
    } 
    } 

    cout << i<<"\t"; 
    return 0; 
} 

Tras el toque de Martin Beckett en el comentario más abajo, en lugar de declarar k dentro del bucle, también se puede declarar k const y fuera del bucle.

De lo contrario, ejd es correcto: aquí el problema no parece una mala paralelización, sino una mala optimización cuando el código se paraleliza. Recuerde que la implementación OpenMP de gcc es bastante joven y dista de ser óptima.

+2

Eso, y no me sorprendería si el compilador optimizara completamente el bucle interno completo con todas las llamadas a 'pow()' en el caso no OMP, ya que puede demostrar que k es constante y un bucle con 16 iteraciones es dentro de la profundidad de desenrollado predeterminada. Las versiones recientes de gcc no tienen ningún problema evaluando largos cálculos en 'double's en tiempo de compilación sin ninguna pérdida de precisión. – Damon

+0

Gracias olenz. Mover la declaración de k al interior del ciclo resolvió el problema de velocidad. Sin embargo, requiere la re-declaración de k por 30000000 veces dentro del ciclo. Intenté una solución diferente manteniendo la declaración de k antes del ciclo (como el código original) y cambié el código de OpenMP a "#pragma omp parallel para firstprivate (k) reduction (+: i)", por lo que k ya no se comparte. Sin embargo, no funcionó. El programa sigue siendo 20 veces más lento, aunque k es el primero en privado. ¿Por qué? – Duncan

+0

Convertir "k" en privado no hace ninguna diferencia en mis ejecuciones (lo cual tiene sentido porque nunca se cambia). Al observar el código generado, el caso de serie se optimiza de manera bastante diferente a la versión de OpenMP. Eso es lo que está dando la gran diferencia de rendimiento. La versión OpenMP todavía está haciendo todos los cálculos en tiempo de ejecución mientras que la versión en serie está haciendo mucho del trabajo en tiempo de compilación. Se ha hecho más trabajo para optimizar el código de serie que el código paralelo (por lo que hay casos en que el serial se ejecuta más rápido que el paralelo, incluso si no hay ninguna razón para que el código paralelo no pueda optimizarse mejor). – ejd

3

código más rápido:

for (int i = 0; i < 100000000; i ++) {;} 

código ligeramente más lento: Código

#pragma omp parallel for num_threads(1) 
for (int i = 0; i < 100000000; i ++) {;} 

2-3 veces más lento:

#pragma omp parallel for 
for (int i = 0; i < 100000000; i ++) {;} 

no importa lo que está entre {y}. Un simple ; o un cálculo más complejo, los mismos resultados. Recopilé bajo Ubuntu 13.10 de 64 bits, usando tanto gcc como g ++, probando diferentes parámetros -ansi -pedantic-errors -Wall -Wextra -O3, y ejecutándome en un Intel quad-core de 3.5GHz.

Supongo que la sobrecarga de administración de subprocesos tiene la culpa? No parece inteligente para OMP crear un hilo cada vez que lo necesite y destruirlo después. Pensé que habría cuatro (u ocho) hilos ejecutándose cuando fuera necesario o durmiendo.

0

Estoy observando un comportamiento similar en GCC. Sin embargo, me pregunto si en mi caso está relacionado de alguna manera con la plantilla o la función en línea. ¿Su código también está dentro de la plantilla o en la función en línea? Por favor mira here.

Sin embargo, para muy corto para los bucles, se puede observar una pequeña sobrecarga relacionada con hilo de conmutación como en su caso:

#pragma omp parallel for 
for (int i = 0; i < 100000000; i ++) {;} 

Si el bucle se ejecuta para algunas de gravedad mucho tiempo como pocos ms o incluso segundos, se debería observar el aumento de rendimiento cuando se usa OpenMP. Pero solo cuando tienes más de una CPU. Cuantos más núcleos tenga, mayor rendimiento alcanzará con OpenMP.

Cuestiones relacionadas