Quand appeler cudaDeviceSynchronize?

quand est-ce que l’appel à la fonction cudaDeviceSynchronize est vraiment nécessaire?

Si je comprends bien la documentation CUDA, les kernelx CUDA sont asynchrones, il semble donc que nous devrions appeler cudaDeviceSynchronize après chaque lancement du kernel. Cependant, j’ai essayé le même code (réseaux de neurones d’apprentissage) avec et sans cudaDeviceSynchronize , sauf un avant la mesure du temps. J’ai trouvé que j’obtiens le même résultat, mais avec une vitesse comprise entre 7 et 12x (en fonction de la taille des masortingces).

La question est donc de savoir s’il existe des raisons d’utiliser cudaDeviceSynchronize dehors de la mesure du temps.

Par exemple:

  • Est-il nécessaire de copier les données du GPU vers l’hôte avec cudaMemcpy ?

  • Si je fais des multiplications de masortingce comme

     C = A * B D = C * F 

dois-je mettre cudaDeviceSynchronize entre les deux?

De mon expérience Il me semble que non.

Pourquoi cudaDeviceSynchronize ralentit-il tellement le programme?

Bien que les lancements du kernel CUDA soient asynchrones, toutes les tâches liées au GPU placées dans un stream (qui est le comportement par défaut) sont exécutées de manière séquentielle.

Ainsi, par exemple,

 kernel1<<>>(...); // kernel start execution, CPU continues to next statement kernel2<<>>(...); // 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 

Donc, dans votre exemple, cudaDeviceSynchronize n’est pas nécessaire. Cependant, il peut être utile pour le débogage de détecter lequel de votre kernel a provoqué une erreur (le cas échéant).

cudaDeviceSynchronize peut causer un ralentissement, mais 7-12x semble trop. Peut-être y a-t-il un problème avec la mesure du temps, ou peut-être que les kernelx sont vraiment rapides, et la synchronisation explicite est énorme par rapport au temps de calcul réel.

Une des situations dans lesquelles l’utilisation de cudaDeviceSynchronize() est appropriée est lorsque plusieurs cudaStream sont en cours d’exécution et que vous souhaitez qu’ils échangent des informations. Un cas réel de ceci est la trempe parallèle dans les simulations quantiques de Monte Carlo. Dans ce cas, nous voudrions nous assurer que chaque stream a fini d’exécuter un ensemble d’instructions et obtenu des résultats avant de commencer à se transmettre des messages, sinon nous finirions par transmettre des informations inutiles. La raison pour laquelle cette commande ralentit le programme est que cudaDeviceSynchronize() force le programme à attendre que toutes les commandes précédemment émises dans tous les stream sur le périphérique se terminent avant de continuer (à partir du CUDA C Programming Guide). Comme vous l’avez dit, l’exécution du kernel est généralement asynchrone. Ainsi, pendant que le périphérique GPU exécute votre kernel, le processeur peut continuer à travailler sur d’autres commandes, donner plus d’instructions au périphérique, etc., au lieu d’attendre. Toutefois, lorsque vous utilisez cette commande de synchronisation, le processeur est forcé de restr inactif jusqu’à ce que tout le travail du processeur graphique soit terminé avant de faire quoi que ce soit d’autre. Ce comportement est utile lors du débogage, car vous pouvez avoir un segfault apparaissant à des moments apparemment “aléatoires” en raison de l’exécution asynchrone du code de périphérique (que ce soit dans un stream ou plusieurs). cudaDeviceSynchronize() forcera le programme à s’assurer que les kernelx / memcpys du stream sont complets avant de continuer, ce qui peut faciliter la recherche de l’emplacement des access illégaux (car l’échec apparaîtra lors de la synchronisation).

Lorsque vous souhaitez que votre GPU commence à traiter certaines données, vous effectuez généralement une invocation interne. Lorsque vous le faites, votre appareil (le GPU) commencera à faire ce que vous lui avez demandé de faire. Cependant, contrairement à un programme séquentiel normal sur votre hôte (le CPU), il continuera à exécuter les lignes de code suivantes dans votre programme. cudaDeviceSynchronize fait que l’hôte (le CPU) attend que le périphérique (le GPU) ait fini d’exécuter TOUS les threads que vous avez démarrés et que votre programme continue comme s’il s’agissait d’un programme séquentiel normal.

Dans les petits programmes simples, vous utiliseriez généralement cudaDeviceSynchronize, lorsque vous utilisez le GPU pour effectuer des calculs, afin d’éviter des erreurs de synchronisation entre le processeur demandant le résultat et le GPU finalisant le calcul. Utiliser cudaDeviceSynchronize le rend beaucoup plus facile à coder pour votre programme, mais il y a un inconvénient majeur: votre processeur est inactif tout le temps, tandis que le GPU effectue le calcul. Par conséquent, en informatique haute performance, vous vous efforcez souvent de faire en sorte que votre processeur effectue des calculs en attendant que le GPU se termine.