¿Las sentencias If-ralentizan mi sombreador?


Quiero saber si "If-statements" dentro de shaders (vértice / fragment / pixel...) están ralentizando el rendimiento del sombreador. Por ejemplo:

Es mejor usar esto:

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

En lugar de usar esto:

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

En otro foro hubo una charla sobre eso (2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html Aquí los chicos están diciendo, que las declaraciones If son realmente malas para el rendimiento de la sombreado.

También aquí están hablando de cuánto hay dentro de las declaraciones if/else (2012): https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-(-)

Tal vez el hardware o el shadercompiler son mejores ahora y arreglan de alguna manera este problema de rendimiento (tal vez no existente).

EDITAR:

Lo que es con este caso, aquí digamos que enable es una variable uniforme y siempre se establece en 0:

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

Creo que aquí tenemos una rama dentro del sombreador que ralentiza el sombreador. ¿Es correcto?

¿Tiene más sentido hacer 2 shaderes diferentes como uno para el else y el otro para la parte if?

Author: Thomas, 2016-06-15

2 answers

¿Qué pasa con los shaders que incluso potencialmente causan problemas de rendimiento en las declaraciones if? Tiene que ver con cómo se ejecutan los shaders y de dónde obtienen las GPU su rendimiento informático masivo.

Las invocaciones separadas de sombreadores generalmente se ejecutan en secuencia, ejecutando las mismas instrucciones al mismo tiempo. Simplemente los están ejecutando en diferentes conjuntos de valores de entrada; comparten uniformes, pero tienen diferentes variables internas. Un término para un grupo de shaders todos ejecutar la misma secuencia de operaciones es "frente de onda".

El problema potencial con cualquier forma de ramificación condicional es que puede arruinar todo eso. Hace que diferentes invocaciones dentro del frente de onda tengan que ejecutar diferentes secuencias de código. Ese es un proceso muy costoso, por el que se debe crear un nuevo frente de onda, copiar datos en él, etc.

A menos que... no lo hace.

Por ejemplo, si la condición es una que se toma por cada invocación en el frente de onda, entonces no se necesita divergencia de tiempo de ejecución. Como tal, el costo de if es solo el costo de verificar una condición.

Hay varios casos aquí, que representan cómo se ve su código para el compilador:

  • Ramificación estática. La condición se basa en constantes de tiempo de compilación; como tal, usted sabe al mirar el código y saber qué ramas se tomarán. Prácticamente cualquier compilador maneja esto como parte de basic optimización.
  • Ramificación estáticamente uniforme. La condición se basa en expresiones que involucran solo uniformes o constantes. No puede saber a priori qué rama se tomará, pero el compilador puede estar estáticamente seguro de que los frentes de onda nunca se romperán con este if.
  • Ramificación dinámica. La condición se basa en expresiones que involucran algo más que constantes y uniformes. Aquí, un compilador no puede decir a priori si un frente de onda se romperá o no. Si esto depende de la evaluación en tiempo de ejecución de la expresión condition.

Diferentes hardware pueden manejar diferentes tipos de ramificación sin divergencia.

También, incluso si una condición es tomada por diferentes frentes de onda, uno podría reestructurar el código para no requerir una ramificación real . Usted dio un buen ejemplo: output = input*enable + input2*(1-enable); es funcionalmente equivalente a la instrucción if. Un compilador podría detectar que un if se está utilizando para establecer una variable, y por lo tanto ejecutar ambos lado. Esto se hace con frecuencia para casos en los que el hardware no puede decir si una condición se puede ejecutar sin divergencia, pero los cuerpos de las dos condiciones son pequeños.

Prácticamente todo el hardware puede manejar var = bool ? val1 : val2 sin tener que divergir. Esto fue posible en 2002.

Dado que esto es muy dependiente del hardware, es... depende del hardware. Hay sin embargo ciertas épocas del hardware que se pueden mirar:

Escritorio, Pre-D3D10

Ahí, es un poco del salvaje oeste. El compilador de NVIDIA para este tipo de hardware era conocido por detectar tales condiciones y en realidad recompilar su sombreador cada vez que cambiaba los uniformes que afectaban a tales condiciones.

En general, esta era es de donde proviene aproximadamente el 80% de las sentencias "never use if". Pero incluso aquí, no es necesariamente cierto.

Puede esperar una optimización de la ramificación estática. Puedes esperar que la ramificación estáticamente uniforme no cause ninguna ralentización adicional (aunque el hecho de que NVIDIA pensara que la recompilación sería más rápida que ejecutarla hace que sea poco probable, al menos para su hardware). Pero la ramificación dinámica le costará algo, incluso si todas las invocaciones toman la misma rama.

Los compiladores de esta era hacen todo lo posible para optimizar los sombreadores para que las condiciones simples se puedan ejecutar de forma sencilla. Por ejemplo, su output = input*enable + input2*(1-enable); es algo que un compilador decente podría generar a partir de su equivalente if instrucción.

Escritorio, Post-D3D10

El hardware de esta era es generalmente capaz de manejar sentencias de ramas estáticamente uniformes con poca desaceleración. Para la ramificación dinámica, puede o no encontrar desaceleración.

Escritorio, D3D11 +

El hardware de esta era está prácticamente garantizado para poder manejar condiciones dinámicamente uniformes con pocos problemas de rendimiento. De hecho, ni siquiera tiene que ser dinámicamente uniforme; siempre y cuando todos los las invocaciones dentro del mismo frente de onda toman la misma ruta, no verá ninguna pérdida de rendimiento significativa.

Tenga en cuenta que algunos hardware de la época anterior probablemente podrían hacer esto también. Pero este es el que es casi seguro que sea cierto.

Móvil, ES 2.0

Bienvenido de nuevo al salvaje oeste. Aunque a diferencia del escritorio Pre-D3D10, esto se debe principalmente a la gran variación del hardware del calibre ES 2.0. Hay una gran cantidad de cosas que pueden manejar ES 2.0, y ellos todos funcionan de manera muy diferente entre sí.

Es probable que se optimice la ramificación estática. Pero si obtiene un buen rendimiento de la ramificación uniforme estática depende mucho del hardware.

Móvil, ES 3.0 +

El hardware aquí es bastante más maduro y capaz que ES 2.0. Como tal, puede esperar que las ramas estáticamente uniformes se ejecuten razonablemente bien. Y algunos hardware probablemente pueden manejar ramas dinámicas como lo hace el hardware de escritorio moderno.

 69
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-22 15:52:27

Es altamente dependiente del hardware y de la condición.

Si su condición es uniforme : no se moleste, deje que el compilador se ocupe de ella. Si su condición es algo dinámico (como un valor calculado a partir de un atributo o obtenido de una textura o algo así), entonces es más complicado.

Para este último caso, prácticamente tendrá que probar y comparar, ya que dependerá de la complejidad del código en cada rama y cuán 'consistente' sea la decisión de la rama ser.

Por ejemplo, si una de las ramas toma el 99% del caso y descarta el fragmento, lo más probable es que desee mantener el condicional. Pero OTOH en su simple ejemplo anterior si enable es alguna condición dinámica, la selección aritmética podría ser mejor.

A menos que tenga un caso claro como el anterior, o a menos que esté optimizando para una arquitectura conocida fija, probablemente sea mejor que el compilador lo averigüe por usted.

 5
Author: 246tNt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-15 06:18:34