2012-05-15 8 views
6

mayoría de las veces se requiere una rama en un programa CUDA o OpenCL, como:CUDA/openCL; ramas de reescritura como no ramificación expresión

for (int i=0; i<width; i++) 
{ 
    if(i % threadIdx.x == 0) 
    quantity += i*i; 
} 

el código puede siempre (o al menos, la mayor parte del tiempo) reescribirse en estilo no-ramificación:

for (int i=0; i<width; i++) 
{ 
    quantity += i*i* (i % threadIdx.x != 0); 
} 

la disyuntiva parece ser ya sea corriendo en una sola ranura urdimbre frente hacer más cálculos en todos los hilos (en el segundo caso, la suma se ejecuta siempre, sólo que a veces el valor es cero)

Suponiendo que las operaciones de bifurcación tomarán múltiples ranuras de urdimbre para cada posible bifurcación, uno esperaría que el segundo sea consistentemente mejor que el primero, ahora mi pregunta es; ¿Puedo confiar en que el compilador optimice 1) en 2) cuando tenga sentido, o no exista un criterio de aplicación amplia, lo que implica que no se puede decidir en general cuál es mejor sin intentar y perfilar?

+0

¿Qué orden es el ancho? Si sabe que el ancho es bastante grande, no debe iterar a través de un ciclo for para hacerlo, ya que sabe qué valores va a utilizar. 'Mientras (i 3Pi

Respuesta

0

No tengo muchas memorias sobre CUDA, pero ¿por qué no paralelizas tu ciclo? Debería usar operaciones atómicas [1] para agregar su cálculo. ¡Espero que esto ayude! Lo siento si no es el caso.

  1. atómicos Operaciones: http://supercomputingblog.com/cuda/cuda-tutorial-4-atomic-operations/
+0

Esto está dentro de un núcleo: cada subproceso está ejecutando el ciclo completo. El comentario no tiene sentido. –

1

En mi experiencia - es totalmente hasta el compilador-escritores para optimizar este tipo de casos extremos.

Entonces, ¿puedo pensar en algún caso donde 1) no se puede convertir en 2)? He aquí uno: escribí núcleos donde era más eficiente ejecutar ciertas partes de los cálculos cada 10 hilos o algo así, en cuyo caso no se puede inferir tal optimización aunque exista una operación matemática (una división y un resta) que pueda arroje el mismo resultado independientemente del resultado condicional en comparación con "ejecutar en todos menos arrojar cero".

Sin embargo, incluso dado que la comprobación de threadId == 0 es un escenario bastante común, no tengo idea de si está realmente optimizado para. Apostaría a que depende de la implementación E incluso del dispositivo en sí (CPU vs GPU).

Tendrás que probarlo para descubrir realmente qué funciona mejor, no solo por el motivo anterior sino también porque el programador del trabajo puede comportarse de manera diferente en función de lo caro que es programar/iniciar/detener un conjunto de hilos en lugar de tenerlos a todos corriendo (y la mayoría proporciona un resultado de cero/identidad).

Espero que esto ayude!

+0

por lo que, en su experiencia, ¿puede hacer algunas recomendaciones con respecto a si siempre trato de escribir código en el estilo 2, suponiendo el peor escenario? o puede eso tener consecuencias involuntarias? – lurscher

+0

No puedo justificar uno sobre el otro en todos los casos, que es mi punto. Probablemente usaría 1) si estuviera haciendo algo así como una reducción de algo manejable en un dispositivo CPU, pero 2) si estuviera en una GPU debido al costo de ramificación en un hardware anterior, si ese escenario fuera válido. Los factores a considerar son: tipo de dispositivo, cuán no paralela es la computación, es factible dividir el cálculo en múltiples kernels (quizás el reducido) y finalmente si la sobrecarga de bifurcación en todo el hardware para el tipo de dispositivo seleccionado es aceptable. Pero IMO, la experimentación siempre sería recomendable. – Ani

+0

para ser claros, estoy hablando en el caso específico de los dispositivos GPU, obviamente no hay ganancia con la CPU porque hay una gran cantidad de predicción de bifurcación y canalización que ayudan con la latencia que oculta – lurscher

3

Las operaciones del módulo son razonablemente costosas: estoy razonablemente seguro de que agregar el módulo requeriría más tiempo que el de tener una sola instrucción que solo se ejecuta 1 subproceso. Su única declaración de bifurcación, if sin else, solo bloqueará los otros hilos mientras se esté ejecutando dicha declaración. Debido a que los gpus están optimizados para un cambio de contexto muy rápido, debe haber muy poco costo para eso.

Sin embargo, se recomienda no utilizar largas instrucciones de bifurcación: un cálculo en serie demasiado grande en la GPU (es decir, un hilo haciendo todo el trabajo) niega la ventaja del paralelismo.

+0

Además, solo verificando la guía de programación CUDA Best, hacer que su código sea fácil de usar para Branch Prediction es de baja prioridad. Hay cosas más importantes para optimizar en general. – 3Pi

Cuestiones relacionadas