2012-08-09 16 views
41

cuando llama a la función cudaDeviceSynchronize realmente necesitada ?.¿Cuándo llamar a cudaDeviceSynchronize?

Por lo que entiendo de la documentación CUDA, los núcleos CUDA son asíncronos, por lo que parece que debemos llamar al cudaDeviceSynchronize después de cada lanzamiento del núcleo. Sin embargo, he intentado con el mismo código (redes neuronales de entrenamiento) con y sin cudaDeviceSynchronize, excepto uno antes de la medición del tiempo. He descubierto que obtengo el mismo resultado pero con una velocidad entre 7-12x (dependiendo de los tamaños de la matriz).

Entonces, la pregunta es si hay alguna razón para usar cudaDeviceSynchronize aparte de la medición de tiempo.

Por ejemplo:

  • es necesario utilizarlo antes de copiar los datos de la GPU de vuelta al servidor con cudaMemcpy?

  • si hago multiplicaciones de matrices como

    C = A * B 
    D = C * F 
    

debería poner cudaDeviceSynchronize entre ambos?

De mi experimento Parece que no.

¿Por qué cudaDeviceSynchronize ralentiza tanto el programa?

+0

Una instancia sería si tiene alguna instrucción de impresión en el kernel, el búfer no se imprimirá hasta un evento de sincronización. –

Respuesta

12

Una situación donde usar cudaDeviceSynchronize() es apropiado sería cuando tiene varios cudaStream s ejecutándose, y le gustaría que intercambien cierta información. Un caso real de esto es el templado paralelo en simulaciones cuánticas de Monte Carlo. En este caso, querríamos asegurarnos de que cada flujo haya terminado de ejecutar un conjunto de instrucciones y obtenido algunos resultados antes de que comiencen a pasar mensajes entre sí, o terminaríamos pasando información basura. El motivo por el que se utiliza este comando ralentiza tanto el programa que cudaDeviceSynchronize() obliga al programa a esperar a que finalicen todos los comandos previamente emitidos en todas las transmisiones del dispositivo antes de continuar (de la Guía de programación de CUDA C). Como dijo, la ejecución del kernel normalmente es asíncrona, por lo que mientras el dispositivo GPU está ejecutando su núcleo, la CPU puede continuar trabajando en otros comandos, emitir más instrucciones al dispositivo, etc., en lugar de esperar. Sin embargo, cuando utiliza este comando de sincronización, la CPU se ve forzada a inactiva hasta que todo el trabajo de la GPU se haya completado antes de hacer cualquier otra cosa. Este comportamiento es útil cuando se depura, ya que puede tener un segfault en tiempos aparentemente "aleatorios" debido a la ejecución asincrónica del código del dispositivo (ya sea en una secuencia o en muchas). cudaDeviceSynchronize() obligará al programa a asegurarse de que los kernels/memcpys de la secuencia estén completos antes de continuar, lo que puede facilitar el descubrimiento de dónde están ocurriendo los accesos ilegales (ya que la falla se mostrará durante la sincronización).

43

Aunque los inicios del kernel CUDA son asincrónicos, todas las tareas relacionadas con la GPU ubicadas en una secuencia (que es el comportamiento predeterminado) se ejecutan secuencialmente.

Así, por ejemplo,

kernel1<<<X,Y>>>(...); // kernel start execution, CPU continues to next statement 
kernel2<<<X,Y>>>(...); // kernel is placed in queue and will start after kernel1 finishes, CPU continues to next statement 
cudaMemcpy(...); // CPU blocks until ememory is copied, memory copy starts only after kernel2 finishes 

Así que en su ejemplo no hay necesidad de cudaDeviceSynchronize. Sin embargo, podría ser útil para la depuración detectar cuál de los kernels ha provocado un error (si hay alguno).

cudaDeviceSynchronize puede causar cierta desaceleración, pero 7-12x parece demasiado.Podría haber algún problema con la medición del tiempo, o puede ser que los núcleos sean realmente rápidos, y la sobrecarga de la sincronización explícita es enorme en relación con el tiempo de cálculo real.

+0

"nvcc" no siempre retiene la "única corriente de GPU predeterminada a menos que se especifique lo contrario". Acabo de depurar un programa en el que descomponía un cálculo extenso en un kernel en un cálculo por partes que lanzaba kernels uno a la vez en un ciclo for(). Los sucesivos inicios del kernel de bucle() reinician donde dejó el kernel anterior de bucle() en el lado del dispositivo. El error fue que el compilador de nvcc no podía ver esto solo con el código de host e intentó ejecutar cada núcleo al mismo tiempo. Esto significaba que todos los kernels pero el primer núcleo estaban computando basura. – opetrenko

+2

@opetrenko Esa no es la forma en que funciona CUDA. –

+0

@ AleksandrDubinsky Por favor, lea mi comentario con más cuidado. Dejo muy explícitamente de que "nvcc" no siempre lo tiene en cuenta. Luego di un ejemplo de un error específico que perseguí utilizando cuda-gdb, que sirve como ejemplo para demostrar precisamente eso. Definitivamente estoy de acuerdo en que, basado en la literatura de Nvidia, no es así como debería funcionar CUDA ... pero lo que estaba diciendo no era una opinión: era una observación hecha durante la depuración sobre cómo funcionaba en una instancia específica. – opetrenko

3

Cuando desee que su GPU comience a procesar algunos datos, normalmente realiza una invocación de kernal. Al hacerlo, su dispositivo (la GPU) comenzará a hacer lo que sea que le indicó que hiciera. Sin embargo, a diferencia de un programa secuencial normal en su host (La CPU) continuará ejecutando las siguientes líneas de código en su programa. cudaDeviceSynchronize hace que el host (la CPU) espere hasta que el dispositivo (la GPU) haya terminado de ejecutar TODOS los hilos que haya iniciado, y así su programa continuará como si fuera un programa secuencial normal.

En programas simples pequeños, normalmente usaría cudaDeviceSynchronize, cuando usa la GPU para hacer cálculos, para evitar discrepancias de tiempo entre la CPU que solicita el resultado y la GPU que finaliza el cálculo. Para usar cudaDeviceSynchronize hace que sea mucho más fácil codificar su programa, pero hay un inconveniente importante: su CPU está inactiva todo el tiempo, mientras que la GPU realiza el cálculo. Por lo tanto, en la informática de alto rendimiento, a menudo se esfuerza para que su CPU realice cálculos mientras espera que termine la GPU.

Cuestiones relacionadas