No sabia si hacer esta entrada por el hecho que no es de alto interés y es un poco complicada. En realidad es un remaster de una entrada que hice hace un tiempo en el otro blog para explicar como funcionan las Compute Units de una GPU contemporánea, pero especialmente para que veais un problema que tienen todas las consolas del mercado, aunque me centrare en esta entrada en todas menos en Switch ya que me es mucho más fácil.

Una Compute Unit de la arquitectura GCN incluye cuatro unidades SIMD16, cada una de ellas tienen unos 64KB que almacenan en su interior registros de 32 bits cada uno, por lo que cada unidad SIMD tiene asignado uno 16.384 registros cada una… ¿Os suena de haberlo visto en otro sitio?

La diferencia principal entre AMD y Nvidia son las llamada olas, en AMD el tamaño de las olas tiene un máximo de 64 elementos mientras que en Nvidia es de 32. Una ola es la unidad minima planificable que el planificador de una Compute Unit puede manejar, en el caso AMD el diagrama muestra un solo planificador pero realmente son 4 planificadores que son lo que le van mandando las olas a ejecutar a cada una de las unidades SIMD pero estas no son enviadas de manera simultanea a todas la unidades SIMD sino que se envía una ola a cada unidad SIMD por ciclo pero esto no significa que la ola sea resuelta en un solo ciclo, en realidad una ola se subidivide siempre en 4 grupos que se ejecutan de manera consecutiva.

¿Pero de donde salen las olas? Un workgroup es un grupo que como mucho tiene 1024 hilos pero un hilo en una GPU no es lo mismo que un contexto en una CPU sino que se trata de una instrucción+su dato asociado. La instrucción puede tener un valor que se ejecuta inmediatamente desde el registro o tener un puntero que apunte a un dato en concreto que hace que se busque el dato en el resto de niveles de la memoria. Como el hecho de tener centenares o incluso miles de ciclos esperando por un dato de un sistema de memoria en común sería desesperante lo más habitual es que excepto que se accede al dato de una textura externa, en el resto de etapas donde no es necesario se utilizan workgroups de acceso automático.

Para que os hagáis una idea, los hilos en una GPU son muy efímeros ya que son de una sola instrucción. Cuando una Compute Unit ha terminado de ejecutar un Workgroup correspondiente a una etapa del pipeline lo que hacen es subir los datos al autobus y enviarlos hacía otra Compute Unite donde esos datos se asociarán directamente a otro Workgroup o a una unidad de función fija o incluso dentro de la propia Compute Unit enviando los datos resultantes a la misma Compute Unit utilizando la Local Data Share para ello.

Pero lo que nos interesa es la forma en la que las Compute Units trabajan con los Workgroups, en realidad cada Compute Unit puede operar con 1 o 2 Workgroups y en el segundo caso dependerá de la cantidad de registros libres que hayan, cada ALU de la unidad SIMD esta asignado a unos registros en concreto. Cuando un Workgroup de hasta 1024 elementos entra en una Compute Unit lo que hace el planificador es subdividirlo en 4 bloques distintos… Uno para cada unidad SIMD y después lo subdivide a través de un valor en olas de diferentes tamaños.

El tema del tamaño de las olas es curioso, hemos de entender que si enviamos una ola de 64 elementos justos ocuparemos todas las ALUs, dado que tenemos 16 ALUs por SIMD esto significa que tardaremos unos 4 ciclos por ola. Pues bien, unos 1024 hilos se desglosan en 256 hilos por SIMD que se convierten en 4 olas y esas 4 olas como mínimo tardan unos 16 ciclos en resolverse en total.

Ahora bien, hemos de tener en cuenta que hay tres tipos de instrucción:

  • Simples: Que tardan 1 ciclo por ALU e hilo.
  • Complejas: Que tardan 4 ciclos por ALU e hilo
  • Trascendentales: Que tardan 16 ciclos en ejecutarse.

Si un grupo de hilos requiere un dato que no tiene disponible y lo ha de buscar en memoria lo que hace la unidad SIMD es enviarlo una señal al planificador que mueve los datos a otra posición de los registros para ser ejecutados más tarde. Las olas entran en las unidades SIMD como una lista continuada y la unidad SIMD le puede enviar tres señales distintas al planificador:

  • Ocupado (esta ejecutando una ola).
  • Petición de Datos (No tiene el dato para operar la instrucción), el planificador pone la instrucción en Stand-By esperando al dato, la unidad SIMD se encarga de la siguiente lista de instrucciones en la lita para no perder el tiempo.
  • Libre: Los registros de la unidad SIMD están vacios por lo que pueden ser llenados de nuevo. El planificador solo le preguntará a la unidad SIMD cada x ciclos como veréis más adelante.

El planificador con tal de ocultar la latencia con la memoria va a llenar cada 40 ciclos de reloj los registros asociados a la unidad SIMD y esto equivale a unas 10 olas consecutivas. ¿Que es lo que ocurre cuando una unidad SIMD ha hecho todo su trabajo antes de hora, pues no ocurre nada porque no hace nada… Esto lo vais a ver muy fácilmente con una tabla…

CiclosWF64WF48WF40WG36WF32WF28WF24
1-464484036322824
5-864484036322824
9-1264484036322824
13-1664484036322824
17-200124036322824
21-24004036322824
25-2800036322824
29-320000322824
33-36000002824
37-4000000024
Total256204240252256252240
Hilos/Ciclo26202425262524

¿Que son los hilos/ciclo? Pues la media de ALUs de la Compute Unit en funcionamiento por ciclo, es decir… La capacidad de calculo real de las Compute Units nunca llega al 100% y las cifras de n TFLOPS son en su mayoria tasas máximas que nunca se cumplen y hay un desaprovechamiento enorme de los recursos de la GPU bajo el actual paradigma.

¿Funcionan diferente las GPUs de Nvidia? En realidad si pero toda esta entrada es para comentaros un concepto que podríamos llamarlos “Shaders de ancho variable” y que van a resultar un cambio importante de cara al futuro. La idea es que la unidad SIMD16 se puede desglosar en varias unidades SIMD trabajando en paralelo y que no sumen entre todas más de 16 ALUs ocupadas por SIMD y 64 ALUs ocupadas en total… Este concepto se rumoreo antes de la presentación de Vega por una patentes de AMD y lo vais a ver en Cassiopea.

La otra mitad de la historia es que el manejo del tamaño de las olas ya no va depender que el desarrollador este pendiente sino que el planificador desmigara el Workgroup en olas de diferentes tamaños de manera dinámica con tal de que la ocupación de las ALUs sea plena. La unidad SIMD se puede dividir en bloques de 1,2,4, 8 y 16 ALUs cada uno y se pueden combinar diferentes bloques siempre y cuando sumen 16. Pues bien, uno de lo cambios que vamos a ver es que se acabo para los desarrolladores romperse el coco para una mayor ocupación de las unidades SIMD y esto va a significar un aumento considerable del rendimiento incluso con cifras en TFLOPS teoricos muy parecidas… Hasta el punto en que mucha gente cuando veas las especificaciones sobre el papel…

… por lo que preparaos para algunas decepciones. Pero cuando veais que los juegos se ven mejor y funcionan mejor de lo que deberían hacerlo con esas cifras muchos os pondréis en modo…

Porque veréis la cantidad de TFLOPS y no os cuadraran ciertas cosas. Esto significa además que el nuevo planificador no va a llevar un registro contador de programa para toda la SIMD sino que va a llevar uno por cada ALU, esto es lo mismo que ya hemos visto en Nvidia a partir de Volta…

Para terminar la entrada, hay un elemento que me gustaría comentar y que ya habéis visto en Volta y Turing en el caso de Nvidia.

Si descontamos el RT Core de Volta y las Tensor Cores (Volta y Turing) se nos dice que cada unidad SM tiene 64 núcleos CUDA pero si contáis veréis que tenemos unas 128 ALUs en total (64 de enteros y 64 de coma flotante)… ¿Como se explica? Pues se explica con el hecho de que cada hilo en una ola puede utilizar un tipo de unidad u otra pero no ambas al mismo tiempo.

Pues bien, mi hipotesis acerca de la unidad Super-SIMD donde dije que se desglosaba no es correcta… por lo que…

Os explico, la unidad Super SIMD en realidad funciona de la misma manera que los núcleos CUDA de Volta y Turing.

Tenemos que cada ALU de la unidad SIMD esta compuesta por tres sub-unidades SIMD distintas que son 2 Core ALU+1 Side ALU. Pues bien, 1 Core ALU+Side ALU hacen una Full ALU que equivale directamente a las ALUs que ya tenemos en las actuales unidades SIMD pero nos queda siempre una Side ALU libre, la diferencia entre la Side ALU y la Core ALU es que la primera no tiene multiplicadores y la Side ALU se utiliza para ejecutar cierto topo de instrucciones de apoyo.

Ahora bien, el uso de la segunda Side ALU es cuanto menos curioso… Puede recibir los datos directamente de la Full ALU o en cierto casos concretos pueden operar en modo VLIW2 donde son las únicas instrucciones donde la la Full ALU y la Side ALU trabajan en paralelo realizando dos operaciones por instrucción. Esto es una funcionalidad que también tienen Volta y Turing por lo que no es más que AMD poniendose a la altura de Nvidia con Navi.

El otro elemento es la Operand Destination Cache, en realidad es lo que Nvidia llama cache L0 y es privada de cada unidad Super-SIMD y se encuentra al mismo nivel de jerarquia que los registros en ambas arquitecturas, por lo demás la Compute Unit de Cassiopea no va a tener muchos más cambio a nivel de hardware que los explicados en esta entrada, bueno… miento… Va a tener cambios aparte pero no los puedo comentar aunque ya os podéis imaginar por donde van las cosas.

Con esto termino, ya sabéis que tenéis Discord y los comentarios de la entrada para hacer los comentarios pertinentes.

Anuncios