Impact des boucles de longueur variable sur les shaders GPU

9

Son populaire pour rendre le contenu procédural à l'intérieur du GPU par exemple dans la demoscene (dessiner un seul quadruple pour remplir l'écran et laisser le GPU calculer les pixels).

La marche des rayons est populaire:

entrez la description de l'image ici

Cela signifie que le GPU exécute un nombre inconnu d'itérations de boucle par pixel (bien que vous puissiez avoir une limite supérieure comme maxIterations).

Comment le fait d'avoir une boucle de longueur variable affecte-t-il les performances du shader?

Imaginez le simple pseudo-code de défilement des rayons:

t = 0.f;
while(t < maxDist) {
    p = rayStart + rayDir * t;
    d = DistanceFunc(p);
    t += d;
    if(d < epsilon) {
       ... emit p
       return;
    }
}

Comment les différentes familles de GPU (Nvidia, ATI, PowerVR, Mali, Intel, etc.) sont-elles affectées? Vertex shaders, mais particulièrement fragment shaders?

Comment peut-il être optimisé?

Volonté
la source
Malheureusement, cette question est trop difficile à répondre correctement ici. Bien qu'une réponse donne déjà des points à une telle source qui mérite d'être lue (implique une ramification dynamique). +1 pour le "sujet" ..
teodron
1
@teodron ne soyez pas défaitiste! J'espérais que quelqu'un dirait que sur les cartes NVidia, les pixels de l'écran en blocs 8x8 répéteront tous aussi profondément que nécessaire, et que des blocs de 8x8 pixels peuvent être effectués dans n'importe quel ordre, ou quelque chose comme ça; ce n'est pas vrai, c'est juste le genre de sagesse que j'espère que les gens pourront partager. Les liens sur Larrabee, hmm, sont assez indirects.
Will
Il ne semble pas qu'il parle de Larrabee, mais le gars de Stanford a prononcé le même discours deux ans après, en 2010 ( vous pouvez le voir ici ). D'après ses chiffres, compte tenu d'une boucle de temps, je ne comprenais pas si les pixels qui «terminent» leurs calculs plus tôt compensent toute performance. Dans CUDA, les threads attendent à une barrière. Par analogie, que se passe-t-il avec les fils de shader?
teodron
@teodron ouais, j'ai pris ma compréhension de CUDA et appliqué aux GPU; Je suis sûr qu'ils sont au pas, mais j'aimerais que quelqu'un sache jouer le carillon; de toute façon, voici quelque chose de connexe williamedwardscoder.tumblr.com/post/26628848007/rod-marching
Will

Réponses:

8

Il y a eu une belle discussion au GDC 2012 sur la marche des rayons du champ de distance du GPU (et d'autres sujets): http://directtovideo.wordpress.com/2012/03/15/get-my-slides-from-gdc2012/

En ce qui concerne les performances, les dernières cartes graphiques (de classe DX11) exécutent des shaders sur les unités SIMD qui exécutent 32 (NVIDIA) ou 64 (AMD) "threads" en même temps. Ces groupes sont connus sous le nom de chaînes ou de fronts d'onde. Pour les pixel shaders, chaque thread équivaut à un pixel, donc je m'attends à ce que l'unité SIMD traite quelque chose comme un bloc de pixels 8x4 (NVIDIA) ou 8x8 (AMD) ensemble. Le branchement et le contrôle de flux sont effectués par front d'onde, de sorte que tous les threads d'un front d'onde doivent boucler autant de fois que le pixel individuel le plus profond de ce front d'onde. Les masques de voie SIMD désactiveront l'exécution pour les pixels qui sont déjà terminés, mais ils doivent toujours suivre silencieusement le contrôle de flux global du front d'onde. Cela signifie, bien sûr, que le système est beaucoup plus efficace lorsque la ramification est cohérente,

D'après mon expérience, les frais généraux de branche sont toujours assez élevés même si tous les threads de la branche de front d'onde sont identiques. J'ai vu des gains de performances dans certains cas en déroulant la boucle pour amortir une partie des frais généraux de la branche. Cependant, cela dépend de la quantité de travail que vous faites dans chaque itération de boucle, bien sûr. Si le corps de la boucle contient suffisamment de "trucs", le déroulement ne sera pas une victoire.

Nathan Reed
la source
0

En ce qui concerne la ramification dynamique, une remarque supplémentaire (peut être évidente, mais mérite d'être notée pour certaines personnes): elle peut affecter gravement les performances des boucles non déroulées (vous ne pouvez évidemment pas dérouler une boucle s'il y a un nombre non constant d'itérations) .

Gavan Woolery
la source
-4

int s = 0;

maintenant pour (int k = 1; k <= n; k ++) {s + = k;} est le même que s = n * (n + 1) / 2

donc ce n'est pas vrai en général: D

gimp
la source
1
Il se peut que vous obteniez beaucoup de votes négatifs parce que personne ne sait vraiment ce que vous essayez de transmettre ici ou ce que cela a à voir avec la question.
doppelgreener