11.7.1.2 : Fonctionnement d'un GPU



Dans un GPU les données peuvent être stockées dans des tenseurs à une, deux ou trois dimensions et peuvent être mises en correspondance avec des threads qui effectueront des calculs sur ces données. Ces threads sont définis dans des blocs de une, deux ou trois dimensions et sont eux-mêmes inclus dans une grille à une deux ou trois dimensions.

Cette configuration modulaire permet de simplifier l'écriture des fonctions de calculs, appelées kernels. Si deux kernels dans deux threads contiguës sont exécutés sur deux données également contiguës, les échanges de données seront plus efficaces.

Une fois que les différents threads sont définis, ils sont exécutés par le GPU. Les threads sont alors organisés en warps qui contiennent un nombre fixe de threads (par exemple $32$ pour les K20 ou les K80).

Ensuite, le GPU exécute les deux premières instructions des threads de la première warp à la dernière, puis les deux instructions suivantes et ainsi de suite.

De ce point de vue, les threads d'un GPU sont davantage synchronisés que ceux d'un CPU. Cette synchronisation doit être prise en compte par les développeurs bas niveau afin de ne pas dégrader les performances de calcul.

En effet, si un kernel contient une condition, seuls les threads ayant une condition positive vont être exécutés immédiatement. Ensuite, les threads ayant une condition négative seront exécutés. Cela implique qu'une condition coûtera le temps total d'exécution des résultats positif et négatif. On parle de divergence des branches de calcul dans le GPU.

Ainsi, le GPU sera fortement sous-employé à l'exécution de cette condition, ce qu'il est préférable d'éviter.