La relación de los Shaders con el rasterizador

El primer punto que necesitamos tener en cuenta es el tema del rasterizador, en el AMD Vega actual este es un cuello de botella porque los Primitive Shaders pueden procesar 11 primitivas por ciclo pero solo tenemos 4 rasterizadores en la GPU por lo que esas 11 primitivas no pueden ser rasterizadas. ¿Vamos a a ver un aumento en el caso de las GPUs de AMD? Bueno, tened en cuenta que Nvidia en la 1080 Ti hay 6 Rasterizadores.

Por lo que la cantidad de Shader Engines podría aumentar en futuras iteraciones de la arquitetura. En todo caso hay un elemento muy importante a tener en cuenta, la cantidad primitivas/triangulos que pueden ser procesadas por una GPU de AMD en estos momentos es 1/16 de la tasa de relleno y esto tiene una explicación.

Cada fragmento de unos 16 pixeles…

4xGRID.png

Es enviado a la Compute Unit desde el rasterizador, haciendo que cada TMU se encargue de una parte distinta, de 2×2 pixeles para ser más exactos.

4XGRUDTMU.png

Ahora bien, en la arquitectura GCN cada Compute Engine puede procesar unos 40 Wavefronts, dado que estos son procesados de 4 en 4 tenemos unos 10 Wavefronts. ¿La mínima? Unos 4 ciclos de reloj por Wavefront por lo que Compute Unit estará ocupada unos 40 ciclos de reloj.

CUWaveFrontPipeline.png

Pero no todas las unidades de la Shader Unit reciben un fragmento a procesar en paralelo, sino que el rasterizador de cada Shader Engine envía un fragmento a cada CU por cada ciclo de reloj por lo que la distribución queda de la siguiente manera:

TriangleShaderDispatch.png

Pensad que tenemos un rasterizador que puede enviar un fragmento por ciclo. Teniendo en cuenta que se tarda unos 10 ciclos por CU en procesar un fragmento y este como mínimo esta asignado a un Wavefront entonces tenemos la situación en la que colocar más de 10 CUs por Shader Engine resulta contraproducente… En el ciclo 11… ¿A donde enviamos el triangulo? a la CU0 o a la CU10.

¿Pero que hay del pipeline geométrico? La arquitectura GCN puede procesar con un solo CU la geometria para saturar un rasterizador, por lo que con 11 CUs por Shader Engine es suficiente a no ser que utilicemos el resto para computación. ¿Cual es el máximo por Shader Engine en la arquitectura GCN? Unas 16 unidades por Shader Engine (Fiji y Vega). Pero lo que tiene que quedar claro es que dedicar más de 11 CUs por Shader Engine para la generación de gráficos rompe el escalado lineal de rendimiento. ¿Lo que veremos de cara al futuro? Pues obviamente un aumento en la cantidad de Shader Engines, lo que supone que la cantidad de triangulos rasterizados por ciclo aumentaría, pero no sería lo único que recibiría un aumento.

Posibles Configuraciones

En las arquitecturas GCN hay una relación directa entre el ancho de la interfaz de la memoria externa y la cantidad de Shader Engines, por ejemplo en la RX 460 tenemos unos 128 bits de memoria GDDR5 y unos 2 Shader Engines.

AMD-RX-460-Polaris-11-Pro-GPU

El ratio suele ser de un Shader Engine por Memory Controller (MC), si pasamos a una configuración de 256 bits GDDR5 entonces…

amd_polaris-10-block-diagram

Pero la arquitectura GCN no puede ir por el momento más allá de 4 Shader Engines… ¿Ejemplos de ello? El AMD Tonga que tiene una configuración con 6 MCs pero se queda en 4 Shader Engines.

AMD-Tonga-XT-Block-Diagram.jpg

¿Y que ocurre con la memoria HBMn? Su ancho de banda es mucho más alto que la GDDR5… Incluso más alto que una GDDR5 con un bus de 384 bits pero aún así tanto en Fiji como en Vega…

203b

Seguimos limitados a unos Shader Engines y de ahí a que tengamos que sobrealimentar cada Shader Engine hasta llegar a las 16 CUs en vez de repartirlo todo en 6 u 8 Shader Engines que de cara a los gráficos daría mejor rendimiento. Pero hemos de tener en cuenta que existe una relación entre las CUs y la jerarquíade memoria del sistema que es importante tener en cuenta.

Si hacemos «zoom» en cualquier Shader Engine

ShaderEngine

Veremos que cada grupo de Compute Units tiene una linea violeta en vertical. ¿Que es?

gcncache2

gs4106-the-amd-gcn-architecture-a-crash-course-by-layla-mah-34-1024

Tenemos a las CUs organizadas hasta en grupos de 4 o en grupos de 3, que reciben las instrucciones a procesar de la cache de instrucciones. Fijaos como la cantidad de RBEs que hay en cada Shader Engine es el mismo que la cantidad de caches Datos e Instrucciones. Esto se explica por la jerarquía de la cache.

gcncachehierarchy

En las arquitecturas Pre-GCN el camino de gráficos acaba en los RBEs/ROPS… pero estos no están conectados a la Cache L2 de manera directa.

rbe480x

A partir de Vega si que lo están.

VegaL2Cache

Pero el camino de datos sigue siendo más o menos el mismo en general, y seguimos teniendo ese ratio. ¿Y para que tanto rollo? Pues por el hecho que colocar unos 6 Shader Engines significaría aumentar la cantidad de RBEs/ROPS proporcionalmente, es decir… Estariamos hablando de configuraciones de 6 RBEs/ 24 ROPS, 12 RBEs/ 48 ROPS, 18 RBEs/72 ROPS, 24 RBEs/96 ROPS en total, por lo que por Shader Engine sería una configuración de 1, 2, 3 o 4 RBEs respectivamente.

Con esto podemos hacer una tabla bien simple:

RBEs por SE Configuración min (CUs) Configuración máx (CUs)
1 3 4
2 6 8
3 9 12
4 12 16

Dado que necesitamos unas 11 Compute Units y ya Xbox One X (10 CUs) como PS4 Pro (9 CUs) y no podemos ir más abajo las configuraciones disponibles serían las que no están tachadas. En un sistema con unos 6 Shader Engines esto significa que tendríamos las siguientes cantidades de ROPS.

  • 72 ROPS
  • 96 ROPS

¿Cual es el problema?  Con unos 6 Shader Engines el cálculo realizado en la anterior entrada tenemos que rehacerlo de nuevo.

  • (3*10^9)*512 bits*4 chips de memoria HBM de consumo= 768 GB/s
  • 768 GB/s /8 bytes por pixel= 96 Gpixeles/s
  • 96 Gpixeles / 16 pixeles triangulo (media)= 6 Triangulos Rasterizados por ciclo.
  • 6 Triangulos / 6 Rasterizadores= 1 Ghz de velocidad de reloj para la GPU

Si hacemos al calculo del ancho de banda según los ROPS:

96 ROPS * 1 Ghz * 8 bytes por pixel = 768 GB/s

¿El siguiente paso? Hemos de tener en cuenta que cada uno de los Shader Engines ha de ser simetrico con los demás y que cada RBE puede tener conectados unas 3 o 4 Compute Units por lo que las configuraciones posibles son:

  • 3-3-3-3 (12 CUs)
  • 3-3-3-4/4-3-3-3/3-4-3-3/3-3-4-3 (13 CUs)
  • 3-3-4-4/3-4-4-3/4-4-3-3/4-3-3-4 (14 CUs)
  • 3-4-4-4/4-3-4-4/4-4-3-4/4-4-4-3 (15 CUs)
  • 4-4-4-4 (16 CUs)

La potencia obtenida a simple vista sería de:

  • 6 Shader Engines * 12 CU por Shader Engine* 1 Ghz: 9.3 TFLOPS (72 CUs)
  • 6 Shader Engines * 13 CU por Shader Engine* 1 Ghz: 10 TFLOPS (78 CUs)
  • 6 Shader Engines * 14 CU por Shader Engine* 1 Ghz: 10,75 TFLOPS (84 CUs)
  • 6 Shader Engines * 15 CU por Shader Engine* 1 Ghz: 11,52 TFLOPS (90 CUs)
  • 6 Shader Engines * 16 CU por Shader Engine* 1 Ghz: 12.3 TFLOPS (96 CUs)

¿Pero cual es la opción que tendría más números? Pues la que resulte más viable economicamente que sería la de 72 CUs, pero hay un motivo para ello.

La evolución histórica de las consolas PlayStation

  • PlayStation 2 estaba pensada para televisores de 480 lineas y podía texturizar unos 8 pixeles por ciclo de reloj.
  • PlayStation 3 estaba pensada para televisores de 720 lineas (2.25 veces el salto), Sony redondeo el salto a 3 e hizo que se pudiesen texturizar 24 pixeles por ciclo de reloj.
  • PlayStation 4 esta pensada para televisores de 1080 lineas (2.25 veces el salto), Sony redondeo el salto a 3 e hizo que se pudiesen texturizar 72 pixeles por ciclo de reloj.

¿El siguiente salto?

4k-vs-1080p

Tenemos un salto de 4 veces la cantidad de pixeles… Si nos fijamos en que cada Compute Unit tiene 4 unidades de texturas entonces estamos hablando de la configuración de 72 CUs.

 

La Compute Unit del Futuro

Antes de continuar hemos de entender que una GPU es un sistema complejo compuesto por varios procesadores, en realidad lo podríamos comparar con una factoría donde cada fábrica de la misma se encarga de una parte diferente del pipeline gráfico.

GPUGlobal

El diagrama esta altamente simplificado pero queda muy claro que la parte más recurrente de una GPU son los shader o lo que llamamos Compute Units… ¿Dichas partes son un procesador dentro del procesador al tener una unidad de control y una ALU completa. Su trabajo es procesar los procesos que el procesador comandos le va enviando… A estos procesos son llamados «Olas» aunque en la jerga de AMD son llamados Wavefront, un Wavefront esta compuesto por unos 64 elementos distintos de procesamiento pero estos se procesan en grupos de 16 elementos en cada ciclo por lo que el tiempo mínimo de procesamiento de un Wavefront son 4 ciclos de reloj.

No, en esta parte no voy a comentar la NCU del AMD Vega, sino que voy a ir más allá. Si miramos la Compute Unit que hay en GCN Pre-Vega,PS4, Xbox One y Xbox One X…

kaveri-14b

… como la NCU de PS4 Pro y GCN Vega.

NCU

Veremos que hay unas 4 unidades vectoriales SIMD16, en total. Cada una esta asignada a un Wavefront distinto. En realidad cada Wavefront es un hilo de ejecucion en si mismo completamente independiente a los otros 3 cuya única comunicación es posible solamente utilizando los 64KB de Local Data Share dentro de la Compute Unit. Es precisamente la LDS lo que permite combinar el contexto de computación y el gráfico dentro de la propia Compute Unit sin que los datos tengan que salir de la misma. ¿El Planificador/Scheduler? Es la unidad de control.

¿Pero que ocurre con la unidad escalar? Pues se encarga de procesar los datos cuanto estos no operan a nivel de varios operandos (vectorial)… ¿Trabaja en paralelo con las unidades vectoriales? No, y ahí esta la clave de una futura mejora que va mucho más allá de Vega y que fuentes que para mi son muy fidedignas me han dicho que veremos a no mucho tardar (¿Navi?) basadas en aumentar el IPC de la Compute Unit de 1 instrucción por ciclo 2 instrucciones por ciclo. ¿Como? Haciendo que la unidad escalar pueda trabajar en paralelo con las unidades vectoriales.

Actualmente si mi código de la GPU tiene una operación escalar automaticamente esta me ocupará todo un ciclo entero y no podre utilizar las unidades SIMD de la GPU hasta que no haya terminado de ejecutar dicha instrucción. La mejora más simple es por tanto es hacer que ambas unidades se puedan utilizar al mismo tiempo, pero tenemos unos 4 Wavefronts en total ejecutandose en la Compute Unit, por lo que el ideal es asignar una ALU escalar por cada ALU vectorial, quedando la cosa más o menos de la siguiente manera:

NAVICU.png

La leyenda del gráfico es la siguiente:

A Unidad Vectorial
B Unidad Escalar
C Registros Vectoriales
D Registros Escalares
E Memoria Local Compartida
F Planificador
G Unidad de Texturas
H Cache L1 (Texturas)

La mayoría de las aplicaciones que utilizan la GPU dependen en alguna parte del código tarde o temprano de una operación escalar. Las operaciones escalares funcionan con un 1 solo operando por instruccion por lo que dichas instrucciones son altamente ineficientes si se colocan en una SIMD16 (solo aprovechariamos 1/16). Por otro lado las operaciones en FP64 (utiles solo en computación científica se colocan en dicha unidad escalar que puede operar hasta con valores en coma flotante de 64 bits.

Una vez dada esta explicación toca el siguiente punto, revelar las fuentes.

Os recomiendo leerla pero en dicha presentación relatan lo que estoy comentando y se puede ver el siguiente diagrama.

FCU.PNG

¿En que se traduce esto en potencia bruta? Pues que pasaríamos de las 64 unidades por CU a unas 68 Unidades por CU, de las 128 operaciones por ciclo a las 136 operaciones por ciclo.

72 CUs* 136 operaciones por CU* 1 Ghz: 9.8 TFLOPS

El problema de la velocidad de reloj

La velocidad de 1 Ghz es demasiado baja para la GPU si tenemos en cuenta que las CUs en estos momentos pueden alcanzar velocidades de reloj muy por encima. En consolas tenemos el caso de la a punto de ser lanzada Xbox One X con una GPU a 1172 Mhz y creedme, en sobremesa nunca hemos visto la velocidad de reloj de las GPUs ir para atrás pero entonces el ancho de banda de la RAM principal se convertiría en un cuello de botella, algo que ya lo es en PS4 y aún más en PS4 Pro (con Xbox One X entrare más adelante, en su entrada correspondiente) por lo que es un problema que posiblemente no van a resolver.

El problema que tenemos es que nuestro sistema no puede ir más allá de los 768 GB/s de ancho de banda y para conseguir dicha configuración necesitamos 4 chips de memoria HBM de consumo…  ¿Lo podemos conseguir de otra manera? Si, con una configuración GDDR6 de 384 bits que nos daría el mismo ancho de banda, pero en ese caso la estructura de memoria cambiaría por completo y tendríamos contención con la CPU y se eliminaría por completo el HBCC al no haber memoria HBM por el medio.

VegaAlternative.png

Precisamente utilizar una configuración de este tipo mantendría la compatibilidad hacía atrás a nivel gráfico pero no solventaría los problemas de contención. Es más, la propia Sony ha dejado caer que PS5 no será compatible hacía atrás con PS4, por lo que dudo mucho que mantengan la configuración de memoria actual con una parte coherente y la otra no-coherente. ¿O no? Aunque eso lo veremos en la siguiente entrada.

Ante todo esto… ¿Cual será la velocidad de reloj de la GPU? No la se, pero estoy seguro que no más baja de los 1200 Mhz. Unos 11.75 TFLOPS de potencia, aunque personalmente lo redondearía a los 12 TFLOPS de potencia.

Y con esto termino la parte de la GPU… ¿La siguiente? No será tan larga y tan pesada como esta y se basará en la memoria del sistema, tanto la RAM tradicional como la no-volatil. Aunque por el medio es posible que haga una entrada de Feedback o un escenario alternativo porque no solo de AMD viven las consolas y hoy otra alternativa a nivel de GPU que merece la pena comentar.