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.