significa que mclibre hacer estructura else elif ejercicios condicionales condicional comparador como años anidado opengl glsl shader direct3d hlsl

opengl - que - mclibre ejercicios python



¿Las declaraciones If reducen la velocidad de mi sombreador? (2)

Quiero saber si las "declaraciones If" dentro de los sombreadores (vértice / fragmento / píxel ...) realmente 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 se habló de eso (2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html Aquí los chicos dicen que las declaraciones If son realmente malas para el rendimiento del sombreador.

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 solucionan de alguna manera este problema de rendimiento (tal vez no existente).

EDITAR:

¿Qué pasa con este caso, aquí permite decir que habilitar 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 eso correcto?

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


¿Qué tienen los sombreadores que incluso potencialmente hace if declaraciones tengan problemas de rendimiento? Tiene que ver con la forma en que se ejecutan los sombreadores y de dónde obtienen las GPU su rendimiento informático masivo.

Las invocaciones de sombreador separadas 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 sombreadores que ejecutan la misma secuencia de operaciones es "wavefront".

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

A menos que ... no lo haga.

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

Aquí hay varios casos que representan cómo se ve el 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 por mirar el código y saber qué ramas se tomarán. Prácticamente cualquier compilador maneja esto como parte de la optimización básica.
  • Ramificación estáticamente uniforme. La condición se basa en expresiones que implican solo uniformes o constantes. No se puede saber a priori qué rama se tomará, pero el compilador puede estar seguro estáticamente de que los frentes de onda nunca se romperán con esto if .
  • Ramificación dinámica. La condición se basa en expresiones que implican algo más que constantes y uniformes. Aquí, un compilador no puede decir a priori si un frente de onda se dividirá o no. Si eso sucede depende de la evaluación del tiempo de ejecución de la expresión de condición.

Diferentes hardware pueden manejar diferentes tipos de ramificaciones sin divergencia.

Además, 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. Dio un buen ejemplo: output = input*enable + input2*(1-enable); es funcionalmente equivalente a la declaración if . Un compilador podría detectar que un if se usa para establecer una variable, y así ejecutar ambos lados. Esto se hace con frecuencia en los casos en que el hardware no puede determinar si una condición se puede ejecutar sin divergencia, pero los cuerpos de las dos condiciones son pequeños.

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

Como esto depende mucho del hardware, depende del hardware. Sin embargo, hay ciertas épocas de hardware que se pueden examinar:

Escritorio, Pre-D3D10

Ahí, es un poco salvaje oeste. El compilador de NVIDIA para dicho hardware era conocido por detectar tales condiciones y, de hecho, recompilaba su sombreador cada vez que cambiaba los uniformes que afectaban tales condiciones.

En general, esta era es de donde proviene el 80% de las "declaraciones de nunca usar if ". Pero incluso aquí, no es necesariamente cierto.

Puede esperar la optimización de la bifurcación estática. Puedes esperar que la ramificación estáticamente uniforme no cause una desaceleració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 época 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 declaración if equivalente.

Escritorio, Post-D3D10

El hardware de esta era generalmente es capaz de manejar declaraciones de sucursales estáticamente uniformes con poca desaceleración. Para la bifurcación dinámica, es posible que experimente una ralentización o no.

Escritorio, D3D11 +

El hardware de esta época 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 que todas las invocaciones dentro del mismo frente de onda tomen la misma ruta, no verá ninguna pérdida significativa de rendimiento.

Tenga en cuenta que algunos hardware de la época anterior probablemente podrían hacer esto también. Pero este es el único lugar donde 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 enorme variación del hardware ES 2.0-calibre. Hay una gran cantidad de cosas que pueden manejar ES 2.0, y todas funcionan de forma muy diferente.

La ramificación estática probablemente se optimice. Pero si obtiene un buen rendimiento de ramificación estáticamente uniforme depende mucho del hardware.

Móvil, ES 3.0+

Aquí el hardware 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 algo de hardware probablemente puede manejar ramas dinámicas de la misma manera que lo hace el hardware de escritorio moderno.


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

Si su condición es uniforme: no se moleste, deje que el compilador se encargue de eso. 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, tendrá que probar y comparar porque dependerá de la complejidad del código en cada una de las ramas y de cuán "consistente" es la decisión de la sucursal.

Por ejemplo, si una de las sucursales se 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 tengas una caja de corte clara como la anterior, o a menos que estés optimizando para una arquitectura conocida, probablemente estés mejor que el compilador.