2011-10-07 11 views
5

Aquí hay dos pedazos de código de un kernel de OpenCL en el que estoy trabajando; muestran tiempos de ejecución muy diferentes.OpenCL: ¿Por qué el rendimiento difiere mucho entre estos dos casos?

El código es bastante complicado, por lo que lo he simplificado.

Esta versión se ejecuta en menos de un segundo:

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
     double nothing = value1; 
    } 
} 

y esta versión toma alrededor de 38 segundos para ejecutar:

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

Como digo, el código es algo más complicado que esto (hay muchas otras cosas sucediendo en los bucles), pero la variable "nada" realmente se mueve desde inmediatamente antes hasta inmediatamente después del corsé.

Soy nuevo en OpenCL, y no puedo entender qué está pasando, y mucho menos cómo solucionarlo. Huelga decir que el caso lento es realmente lo que necesito en mi implementación. He intentado meterme con los espacios de direcciones (todas las variables aquí están en __private).

Solo puedo imaginar que, por alguna razón, la GPU está presionando la variable "value1" en la memoria más lenta cuando se cierra la abrazadera. ¿Es esta una explicación probable? ¿Que puedo hacer?

¡Gracias de antemano!

ACTUALIZACIÓN: Esto se ejecuta en menos de un segundo también: (pero sin comentario en ninguna de las líneas, vuelve a la lentitud extrema). Esto es sin realizar ningún otro cambio en los bucles, y el valor 1 todavía se declara en el mismo lugar que antes.

for (int ii=0; ii<someNumber;ii++) 
{ 
    for (int jj=0; ii<someNumber2;jj++) 
    { 
//  value1 = value2 + value3; 
//  value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

ACTUALIZACIÓN 2: El código fue realmente anidado en otro bucle de este tipo, con la declaración de value1 como se muestra:

double value1=0; 
for (int kk=0; kk<someNumber3;kk++) 
{ 
    for (int ii=0; ii<someNumber;ii++) 
    { 
     for (int jj=0; ii<someNumber2;jj++) 
     { 
      value1 = value2 + value3; 
      value1 = value1 * someFunction(a,b,c); 
     } 
     double nothing = value1; 
    } 
} 

en movimiento, donde value1 se declara también nos lleva de nuevo a la caja rápida:

for (int kk=0; kk<someNumber3;kk++) 
{ 
    double value1=0; 
    for (int ii=0; ii<someNumber;ii++) 
    { 
     for (int jj=0; ii<someNumber2;jj++) 
     { 
      value1 = value2 + value3; 
      value1 = value1 * someFunction(a,b,c); 
     } 
     double nothing = value1; 
    } 
} 

parece OpenCL es un arte muy difícil! Todavía no entiendo realmente lo que está pasando, pero al menos sé cómo solucionarlo ahora.

+0

Eso es bastante extraño. ¿Estás seguro de que necesitas usar la versión más lenta? A partir de estos fragmentos se ven funcionalmente idénticos. – Chriszuma

+0

Gracias por su respuesta. Sí, estoy seguro, pero tienes razón en que los ejemplos que he dado son funcionalmente idénticos. El código en las llaves internas debería tener un + =. – carthurs

+0

No veo ninguna razón para que el segundo sea más lento en función de esos fragmentos de código. Supongo que mover la tarea debe tener efectos secundarios en alguna parte, como una mayor ramificación (una unidad de trabajo ejecuta el 'si', la siguiente ejecuta el' else'), lo que realmente puede ralentizar la GPU. –

Respuesta

4

¿Qué implementación está utilizando? Yo esperaría el "doble nada = valor1"; para ser eliminado como código muerto en cualquiera de los casos por cualquier compilador razonable.

+0

Creo que he encontrado el problema, gracias a su publicación. En el caso 1 (el primer recuadro de mi pregunta), creo que el compilador optimiza al "eliminar como código muerto" el ciclo interno. En el caso 2, se da cuenta de que la variable 'value1' es necesaria fuera del ciclo interno, por lo que la ejecuta. La función 'someFunction (a, b, c)' es muy lenta, por lo que provoca la ralentización. Para su información, la implementación es el SDK de AMD para Linux. ¡Gracias por ayudar a todos! – carthurs

+0

Usted dice que debido a que value1 no se usa, el compilador optimiza la llamada a alguna función. ¿Cómo puede estar seguro de que algunaFunción no tiene un efecto secundario? – vocaro

+0

Porque 'nada' no se usa. No estaba hablando de value1. – arsenm

2

El primer caso es solo un bucle (con optimizaciones del compilador) Pero el segundo es un bucle con un bucle anidado. Ese es el gran problema. Muchas comprobaciones de las variables globales/locales. (¿Seguro que son privadas? ¿Has declarado todo eso dentro del kernel?)

Te recomiendo que lo guardes como variable privada (somenumber y somenumber2) antes de iniciar el ciclo. Porque de esa manera estarás revisando cada vez con datos privados. Como experiencia personal, cada var utilizada como el caso de verificación de un bucle OpenCL debe ser privada. Esto puede ahorrar hasta 80% de acceso a la memoria global. (Especialmente si el bucle es muy corto o simple)

Como ejemplo, este debe trabajar rápido:

int c_somenumber = someNumber; 
for (int ii=0; ii<c_someNumber;ii++) 
{ 
    int c_somenumber2 = someNumber2;  
    for (int jj=0; ii<c_someNumber2;jj++) 
    { 
     value1 = value2 + value3; 
     value1 = value1 * someFunction(a,b,c); 
    } 
    double nothing = value1; 
} 

EDIT: Además, valor1 debe almacenar en caché en la memoria privada.(Como lo hizo en su última edición)

+0

Sí, todo se guarda como privado. ¡Vea mi explicación del problema como un comentario sobre la otra respuesta! – carthurs

Cuestiones relacionadas