J'ai entendu dire que si les instructions devaient être évitées dans les shaders, parce que les deux parties des instructions seront exécutées et que le mauvais sera supprimé (ce qui nuit à la performance).
C'est toujours un problème dans DirectX 10? Quelqu'un m'a dit que seule la bonne branche serait exécutée.
Pour l'illustration j'ai le code:
float y1 = 5; float y2 = 6; float b1 = 2; float b2 = 3;
if(x>0.5){
x = 10 * y1 + b1;
}else{
x = 10 * y2 + b2;
}
Existe-t-il un autre moyen de le rendre plus rapide?
Si oui, comment faire?
Les deux branches se ressemblent, la seule différence est les valeurs des "constantes" ( y1, y2, b1, b2
sont les mêmes pour tous les pixels dans Pixel Shader).
Réponses:
De nombreuses règles de micro-optimisation des shaders sont les mêmes que pour les CPU traditionnels avec des extensions vectorielles. Voici quelques conseils:
test
,lerp
/mix
)Il est vrai que les succursales sont moins chères sur le matériel moderne qu'elles ne l'étaient auparavant, mais il est toujours préférable de les éviter si possible. En utilisant le swizzling et les fonctions de test, vous pouvez réécrire votre shader sans tests:
L'utilisation de
step
etlerp
est un idiome très courant pour choisir entre deux valeurs.la source
En général, ça va. Les shaders s'exécuteront en groupes de sommets ou pixels (différents fournisseurs ont une terminologie différente pour ceux-ci, donc je m'en tiens à l'écart) et si tous les sommets ou pixels d'un groupe empruntent le même chemin, le coût de branchement est négligeable.
Vous devez également faire confiance au compilateur de shader. Le code HLSL que vous écrivez ne doit pas être considéré comme une représentation directe du bytecode ou même de l'assembly vers lequel il sera compilé, et le compilateur est parfaitement libre de le convertir en quelque chose d'équivalent mais évite la branche (par exemple, un lerp peut parfois être une conversion préférée). D'un autre côté, si le compilateur détermine que l'exécution d'une branche est en fait le chemin le plus rapide, il la compilera jusqu'à une branche. La visualisation de l'assemblage généré dans PIX ou un outil similaire peut être très utile ici.
Enfin, la vieille sagesse tient toujours ici - profilez-la, déterminez si c'est réellement un problème de performances pour vous, et abordez-la ensuite, pas avant. En supposant que quelque chose peut être un problème de performances et en agissant selon cette hypothèse, vous n'encourrez qu'un risque énorme de problèmes plus importants par la suite.
la source
Citation du lien / article publié par Robert Rouhani:
Comme l'a suggéré mh01 ("L'affichage de l'assembly généré dans PIX ou un outil similaire peut être très utile ici."), Vous devez utiliser un outil de compilation pour examiner la sortie. D'après mon expérience, l'outil Cg de nVidia (Cg est encore largement utilisé aujourd'hui en raison de ses capacités multiplateformes) a donné une illustration parfaite du comportement mentionné dans le paragraphe des codes de condition des gemmes du GPU (prédication) . Ainsi, quelle que soit la valeur de déclenchement, les deux branches ont été évaluées par fragment, et seulement à la fin, la bonne a été placée dans le registre de sortie. Néanmoins, le temps de calcul a été perdu. À l'époque, je pensais que la ramification améliorerait les performances, en particulier parce que tousles fragments dans ce shader se sont appuyés sur une valeur uniforme pour décider de la bonne branche - cela ne s'est pas produit comme prévu. Donc, une mise en garde majeure ici (par exemple éviter les ubershaders - peut-être la plus grande source d'enfer de ramification).
la source
Si vous ne rencontrez pas déjà de problèmes de performances, c'est très bien. Le coût de comparaison avec une constante est toujours extrêmement bon marché. Voici une bonne lecture de la branche GPU: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html
Quoi qu'il en soit, voici un extrait de code qui va être bien pire que l'instruction if (et qui est beaucoup moins lisible / maintenable), mais qui s'en débarrasse toujours:
Notez que je fais l'hypothèse que x est limité à la plage
[0, 1]
. Cela ne fonctionnera pas si x> = 2 ou x <0.Ce qui est coupé, c'est convertir x en ou
0
ou1
et multiplier le mauvais par 0 et l'autre par 1.la source
if(x<0.5)
la valeur defx
doit êtreround(x)
oufloor(x + 0.5)
.Il existe plusieurs instructions capables de faire des conditions sans ramification;
Plus certains opérateurs logiques;
source: http://theorangeduck.com/page/avoiding-shader-conditionals
la source