90 votes

Les instructions If ralentissent-elles mon shader?

Je veux savoir si "Si-états" à l'intérieur de shaders (vertex / fragment / pixel...) sont vraiment ralentir le shader de la performance. Par exemple:

Est-il préférable d'utiliser ceci:

vec3 output;
output = input*enable + input2*(1-enable);

au lieu d'utiliser ce:

vec3 output;
if(enable == 1)
{
    output = input;
}
else
{
    output = input2;
}

dans un autre forum il y avait un parler (2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html Ici, les hommes ont à dire, que Si les relevés sont vraiment mauvais pour la performance de l'ombrage.

Ici aussi, ils parlent de la façon dont une grande partie est à l'intérieur du if/else (2012): https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-(-)

peut-être que le matériel ou le shadercompiler sont mieux maintenant et qu'ils fixent en quelque sorte, c' (peut-être n'existant pas) problème de performance.

EDIT:

Qu'est-ce que avec cette affaire, ici, permet de dire que l'option activer est une variable uniforme et il est toujours définie à 0:

if(enable == 1) //never happens
{
    output = vec4(0,0,0,0);
}
else  //always happens
{
    output = calcPhong(normal, lightDir);
}

Je pense que nous avons ici une branche à l'intérieur de l'shader qui slowes le shader vers le bas. Est-ce exact?

Est-il plus judicieux de faire 2 différents shaderes comme l'un pour l'autre et l'autre pour la si la partie?

177voto

Nicol Bolas Points 133791

Qu'est-ce shaders que même les rend potentiellement if des énoncés des problèmes de performances? Il a à voir avec la façon dont les shaders exécuté et où Gpu obtenir leur énorme puissance de calcul de.

Séparer shader invocations sont généralement exécutés dans l'ordre, l'exécution de la même instructions en même temps. Ils sont tout simplement de les exécuter sur différents ensembles de valeurs d'entrée; ils partagent des uniformes, mais ils ont différentes variables internes. Un terme pour un groupe de shaders toutes exécution de la même séquence d'opérations est "front d'onde".

Le problème potentiel avec toute forme de branchements conditionnels, c'est qu'il peut vis de tout ça. Il provoque des différents appels à l'intérieur de l'onde d'avoir à exécuter les différentes séquences de code. C'est un processus très coûteux, en vertu de laquelle un nouveau front d'onde doit être créé, les données copiées sur lui, etc.

À moins que... ça ne marche pas.

Par exemple, si la condition est celle qui est prise par chaque invocation dans le front d'onde, alors pas de divergence d'exécution est nécessaire. En tant que tel, le coût de l' if est juste le coût de la vérification d'une condition.

Il y a plusieurs cas ici, qui représentent ce que votre code ressemble à du compilateur:

  • Statique de la ramification. La condition est basée sur des constantes de compilation; en tant que tel, vous savez, en regardant le code et le savoir branches qui seront prises. Pratiquement n'importe quel compilateur gère cela dans le cadre de l'optimisation de base.
  • De manière statique uniforme de ramification. La condition est basée sur des expressions faisant intervenir uniquement des uniformes ou des constantes. Vous ne pouvez pas savoir a priori la branche va être pris, mais le compilateur peut statiquement être certains que les fronts d'onde ne sera jamais brisé par cette if.
  • Dynamique de la ramification. La condition est basée sur des expressions qui impliquent plus que juste des constantes et uniformes. Ici, un compilateur ne peut pas dire a priori si un front d'onde sera brisé ou pas. Si cela se produit dépend de l'exécution de l'évaluation de l'expression de condition.

Différents matériels peuvent gérer différents types de ramification sans divergence.

Aussi, même si une condition est pris par les différents fronts d'onde, on pourrait restructurer le code pour ne pas nécessiter réelle de ramification. Vous a donné un bel exemple: output = input*enable + input2*(1-enable); est fonctionnellement équivalent à l' if déclaration. Un compilateur pourrait détecter qu'un if est utilisé pour définir une variable, et donc d'exécuter des deux côtés. C'est souvent le cas pour les cas où le matériel ne peut pas dire si un état peut être exécutée sans divergence, mais les corps des deux conditions sont de petite taille.

Quasiment tout ce matériel peut traiter var = bool ? val1 : val2 sans avoir à diverger. Cela a été possible en 2002.

Depuis c'est très matériel dépendant, il... dépend du matériel. Il y a cependant certaines époques de matériel qui peut être regardé:

Bureau, Pré-D3D10

Là, c'est un peu le far west. NVIDIA compilateur pour ce type de matériel était tristement célèbre pour la détection de telles conditions et en fait la recompilation de votre shader chaque fois que vous modifiez les uniformes qui a touché de telles conditions.

En général, cette période est, où près de 80% de "ne jamais utiliser d' if états" vient de. Mais même ici, il n'est pas nécessairement vrai.

Vous pouvez vous attendre à l'optimisation de la statique de la ramification. Vous pouvez l'espoir de manière statique uniforme de branchement ne causera pas de ralentissement supplémentaire (bien que le fait que NVIDIA pensée recompilation serait plus rapide que l'exécution de ce fait, il est peu probable, au moins pour les leur matériel). Mais la dynamique de branchement va vous coûter quelque chose, même si toutes les invocations de prendre la même direction.

Les compilateurs de cette époque font de leur mieux pour optimiser les shaders ainsi que des conditions simples peuvent être exécutées simplement. Par exemple, votre output = input*enable + input2*(1-enable); "est quelque chose qui décent compilateur peut générer à partir de votre équivalent if déclaration.

Bureau, Post-D3D10

Le matériel de cette époque est généralement capable de gérer de manière statique uniforme branches états avec peu de ralentissement. Pour la dynamique de la ramification, vous peut ou peut ne pas rencontrer ralentissement.

Bureau, D3D11+

Le matériel de cette époque, il est presque garanti pour être capable de gérer dynamiquement uniforme conditions avec peu de problèmes de performances. En effet, il n'a même pas à être dynamiquement uniforme; à condition que toutes les invocations dans le même front d'onde prendre le même chemin, vous ne verrez pas de pertes de performances.

Notez que certains matériels de l'époque aurait probablement pu en faire de même. Mais c'est celui où il est presque certain d'être vrai.

Mobile, ES 2.0

Bienvenue à l'ouest sauvage. Mais contrairement à Pré-D3D10 de bureau, c'est principalement en raison de l'énorme écart de ES 2.0-calibre matériel. Il y a une énorme quantité de choses qui peuvent gérer ES 2.0, et ils fonctionnent tous de manière très différente les uns des autres.

Statique de la ramification sera susceptible d'être optimisé. Mais si vous obtenez la bonne performance de manière statique uniforme de branchement est très indépendant du matériel.

Mobile, ES 3.0+

Le matériel ici est un peu plus mature et capable que ES 2.0. En tant que tel, vous pouvez vous attendre de manière statique uniforme branches d'exécuter raisonnablement bien. Et certains matériels peuvent probablement poignée de la dynamique des branches de la façon moderne de bureau matériel ne.

12voto

246tNt Points 987

C'est très dépendante sur le matériel et sur la condition.

Si votre condition est uniforme : ne vous embêtez pas, permettre au compilateur de traiter avec elle. Si votre état est quelque chose de dynamique (comme une valeur calculée à partir de l'attribut ou extraites à partir de la texture ou de quelque chose), ensuite c'est plus compliqué.

Pour ce dernier cas, vous aurez à peu près à l'essai et de référence, car il dépend de la complexité du code dans chacune des branches et comment "cohérence" de la direction de décision.

Par exemple, si l'une des branches est pris 99% des cas, et rejette le fragment, il est alors probable que vous voulez garder le conditionnel. Mais otoh, que dans votre exemple simple ci-dessus s' enable est un certain état dynamique, arithmétique sélectionner peut-être mieux.

À moins que vous ayez une claire cas comme ci-dessus, ou si vous êtes l'optimisation pour un fixe connue architecture, vous êtes probablement mieux de le compilateur figure que pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X