Acerca de los registros de máscara

El conjunto de instrucciones AVX-512 incluía ocho llamados registros de máscara [1], desde k0 [2] hasta k7 . Son adecuados para su uso con la mayoría de las operaciones de ALU y le permiten realizar operaciones de máscara en elementos vectoriales con puesta a cero o fusión de datos en el registro de destino [3], acelerando así el código, lo que requeriría operaciones de fusión adicionales en el conjunto de instrucciones AVX2 y versiones anteriores .

Si lo anterior no es suficiente para convertirte en un seguidor del culto a los registros de máscaras, citaré un extracto del artículo de Wikipedia que, espero, te ayudará finalmente a resolverlo:
La mayoría de los comandos AVX-512 pueden usar la máscara de operando correspondiente a uno de los 8 registros de máscara (k0 - k7). Si el registro de máscara se usa como la máscara de la operación, el registro k0 se comporta de manera diferente que el resto de los registros de máscara: en este caso, actúa como una constante codificada que indica que la máscara no se usa con esta operación. Sin embargo, en operaciones aritméticas y lógicas y al escribir valores para enmascarar registros, k0 se comporta como un registro de trabajo normal. En la mayoría de los comandos, los registros de máscara se usan como una máscara que determina qué elementos deben escribirse en el registro de salida. El comportamiento de la máscara de operando depende del indicador: si se establece, todos los elementos no seleccionados se restablecerán (modo "puesta a cero", cero), si no, todos los elementos no seleccionados conservan su estado anterior (modo de fusión , fusión ). El modo de fusión tiene el mismo efecto que las instrucciones de mezcla .

En general, los registros de máscara [4] son ​​una innovación importante, pero rara vez se recuerdan en contraste con, por ejemplo, registros de propósito general ( eax , rsi y otros) o registros SIMD ( xmm0 , ymm5 , etc.). En las presentaciones de Intel, que muestran los tamaños de los recursos de microarquitectura, no se mencionan los registros de máscara:

imagen

Hasta donde yo sé, nunca se ha publicado información sobre el tamaño del archivo de registro físico ( archivo de registro físico, PRF ) de los registros de máscara. Ahora lo arreglaremos.
Utilicé una versión modificada de la herramienta para medir el tamaño del búfer de reordenamiento de comandos ( ROB ), que fue creado y descrito por Henry Wong [5] (en adelante, simplemente Henry). Usando esta herramienta, calculó el tamaño de las estructuras documentadas y no documentadas de ejecución extraordinaria en arquitecturas anteriores. Si no ha leído la nota de Henry, deténgase y vuelva a leerla. Y mi artículo esperará.

Bueno, lee? Por daño, aquí hay un resumen del artículo de Henry:

ROB


Se insertan varias instrucciones de balasto entre dos instrucciones de lectura con un error de caché [6] ; su número exacto dependerá del recurso del procesador que queramos medir. Si no hay muchos comandos de balasto, ambas fallas de caché se procesarán en paralelo, por lo que sus retrasos se superponen y el tiempo de ejecución total será aproximadamente [7] igual al que tomaría una falla de caché.

Sin embargo, si el número de comandos de lastre supera un cierto umbral crítico, el recurso correspondiente se agotará por completo y la ubicación de los equipos en el ROB cesará antes de que se emita el segundo comando con una falta de caché. En este caso, su procesamiento paralelo será imposible y el tiempo total casi duplicará el tiempo de una de esas operaciones, lo que se reflejará en el gráfico como un salto brusco.

Finalmente, la prueba se escribe para que los equipos usen exactamente una unidad del recurso que se está verificando; en este caso, el pico máximo en el gráfico indicará su volumen total. Por lo tanto, los comandos estándar de propósito general, como regla, usan un registro físico de registros PRF de propósito general y, por lo tanto, son ideales para medir el volumen de un recurso dado.

Tamaño del archivo de registro físico de registros de máscara


En esta prueba, ejecutaremos comandos que escriben el valor para enmascarar registros para averiguar el tamaño PRF de estos registros.

Comencemos con una serie de equipos kaddd k1, k2, k3 (se muestran 16 equipos de lastre):

mov    rcx,QWORD PTR [rcx]  ;    -
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
kaddd  k1,k2,k3
mov    rdx,QWORD PTR [rdx]  ;    -
lfence                      ;      ,    
                            ;   
;     16 

Cada comando kaddd consume un registro de máscara física. Si el número de comandos de balasto es menor o igual que el número de registros de máscara, los errores de caché se procesarán en modo paralelo, de lo contrario, en modo secuencial. Entonces, al cambiar del modo paralelo al serial, deberíamos ver un salto brusco en el gráfico, lo que indica un aumento en el tiempo de ejecución.

Esto es exactamente lo que estamos observando:

imagen

echemos un vistazo más de cerca al aumento:

imagen

como vemos ahora, el salto no es tan agudo : con el número de comandos de lastre de 130 a 134, la velocidad de ejecución toma valores intermedios entre los niveles mínimo y máximo. Henry llama a este comportamiento imperfecto; Lo observé en muchas de estas pruebas, aunque no en todas. El hecho es que la implementación del hardware no siempre permite un agotamiento completo del recurso a medida que se acerca al límite [8]; en algunos casos esto tiene éxito, en otros solo faltan algunos equipos hasta un máximo teórico.
En este sentido, estamos interesados ​​en el penúltimo punto de ascenso, en el que la velocidad es aún mayor que en el modo lento. Este punto indica el número de unidades de recursos disponibles para nosotros, lo que significa que hay al menos tantos registros físicos. Como puede ver, en este caso es de alrededor de 134.

Por lo tanto, SKX tiene 134 registros físicos capaces de almacenar valores especulativos (obtenidos con ejecución anticipada) de registros de máscara. Henry sugiere que se usen 8 más para almacenar el estado arquitectónico actual de ocho registros de máscara, por lo que la cantidad total de su PRF se puede estimar en 142. Esto es ligeramente más pequeño que el tamaño de los archivos para registros de propósito general (180) y registros SIMD (168), pero aún así bastante (ver tabla de tamaños de recursos de ejecución extraordinaria para otras plataformas).

Digamos que este archivo es lo suficientemente grande como para que en la práctica no tengamos tiempo para ocuparlo por completo: es difícil imaginar un código real en el que casi el 60% [9] de los comandos escriben [10] para enmascarar registros, es decir, se requerirá que muchos de ellos agoten este recurso .

¿Son estos diferentes archivos de registro?


Como debe haber notado, hasta ahora he asumido por defecto que los PRF de registro de máscara son un archivo separado que no se cruza con otros tipos de archivos de registro. Creo que esto se basa muy probablemente en el principio de funcionamiento de los registros de máscara y en el hecho de que forman parte de un dominio separado para cambiar el nombre de los registros [11]. Otro argumento a favor de mi suposición es el hecho de que el tamaño observado de los registros de máscara PRF no coincide con el tamaño del archivo de registro de propósito general o el archivo de registro SIMD. En realidad, ¡podemos tomar y probar esto con una prueba!

Esta prueba es similar a la anterior, pero ahora los comandos kadddse alternará con comandos que usan registros de propósito general o registros SIMD. Si los registros de máscara se combinan con el primero o el segundo en el mismo archivo de registro, el salto en el gráfico debe indicar el tamaño del PRF correspondiente. Si los archivos de registro no se fusionan, encontraremos algún otro límite, que no será igual al tamaño de ninguno de los dos archivos de registro, pero será igual, por ejemplo, al tamaño de ROB.

En la prueba 29 , los comandos kaddd y los comandos de agregar escalares se alternan :

mov    rcx,QWORD PTR [rcx]
add    ebx,ebx
kaddd  k1,k2,k3
add    esi,esi
kaddd  k1,k2,k3
add    ebx,ebx
kaddd  k1,k2,k3
add    esi,esi
kaddd  k1,k2,k3
add    ebx,ebx
kaddd  k1,k2,k3
add    esi,esi
kaddd  k1,k2,k3
add    ebx,ebx
kaddd  k1,k2,k3
mov    rdx,QWORD PTR [rdx]
lfence

Observamos el gráfico:

imagen

como puede ver, el número de equipos de lastre, que representa el pico, es mayor que el tamaño de los registros de propósito general de PRF y los registros de máscara. De esto concluimos que los registros de máscara no están incluidos en el archivo de registro de propósito general.

Entonces, ¿tal vez están incluidos en el archivo de registro SIMD? Después de todo, los registros de máscara están más asociados con los comandos SIMD que con los comandos de propósito general.

Para averiguarlo, usaremos la prueba 35, que es idéntica a la prueba 29 con la diferencia de que aquí los comandos kaddd se alternan con los comandos vxorps :

mov    rcx,QWORD PTR [rcx]
vxorps ymm0,ymm0,ymm1
kaddd  k1,k2,k3
vxorps ymm2,ymm2,ymm3
kaddd  k1,k2,k3
vxorps ymm4,ymm4,ymm5
kaddd  k1,k2,k3
vxorps ymm6,ymm6,ymm7
kaddd  k1,k2,k3
vxorps ymm0,ymm0,ymm1
kaddd  k1,k2,k3
vxorps ymm2,ymm2,ymm3
kaddd  k1,k2,k3
vxorps ymm4,ymm4,ymm5
kaddd  k1,k2,k3
mov    rdx,QWORD PTR [rdx]
lfence

Gráfico:

imagen

En esta prueba, se observa el mismo comportamiento que en la anterior, por lo que concluimos que los archivos de registro de registros de máscara y registros SIMD también están separados.

Misterio sin resolver


Sin embargo, en ambas pruebas, el final del pico cae en aproximadamente 212 comandos, mientras que el tamaño de ROB para esta microarquitectura es 224. ¿Quizás es solo un comportamiento imperfecto que ya observamos anteriormente? Bueno, verifiquemos esto: compare los resultados de estas dos pruebas con los resultados de la prueba 4, en la que solo los comandos nop se usan como comandos de lastre : a excepción de ROB, no deberían consumir ningún otro recurso. Compare las gráficas de la prueba 4 ( nop ) y la prueba 29 ( kaddd y scalar add alternate ):

imagen

en la prueba 4El comienzo del modo lento cae exactamente en la marca 224 (imágenes vectoriales, por lo que puede aumentarlas y verlo usted mismo). Resulta que 212 (a partir de este punto, el modo lento comienza cuando se alternan registros de máscara con registros generales o registros SIMD): este es el límite de algún otro recurso. De hecho, encontramos la misma limitación incluso cuando alternamos registros generales y registros SIMD: compare la prueba 4 y la prueba 21 (combina la adición de registros generales y comandos SIMD vxorps ):

imagen

en su artículo , en con el mismo título ("Misterio sin resolver") Henry describe el mismo efecto, pero aún más pronunciado:
, AVS SSE Sandy Bridge 147 , ROB. (, , AVX- , NOP-), , SSE/AVX, , - , 147, – , .
Para más detalles, lo remito al artículo de Henry. Observamos un efecto similar, pero menos pronunciado: al menos logramos ocupar el 95% del volumen ROB, pero aún no lo agotamos por completo. Quizás ese misterioso grupo común de registros está asociado con el mecanismo de su lanzamiento, por ejemplo, una tabla PRRT [12], que realiza un seguimiento de los registros disponibles para su liberación una vez que se completa el comando.

Finalmente, hablemos sobre algunas características más de los registros de máscara y verifiquemos si los mecanismos de optimización disponibles para los registros de propósito general y los registros SIMD son aplicables a ellos.

Copia de reemplazo


Para fines generales o comandos SIMD, se puede aplicar la llamada eliminación de movimiento . Con esta optimización, el mecanismo de cambio de nombre del registro permite no ejecutar comandos que copian el valor de un registro a otro, por ejemplo mov eax , edx o vmovdqu ymm1 , ymm2 ; en cambio , el registro de destino se reasigna "simplemente" [13] al registro de origen en RAT, lo que le permite hacerlo sin involucrar a ALU.

Compruebe si el reemplazo de la copia es aplicable, por ejemplo, al comando kmov k1 , k2 . Primero, mire el gráfico de la prueba 28 , donde kmovd k1 es el equipo de lastre ,k2 :
imagen
Este gráfico se ve exactamente igual que en la prueba 27 discutida anteriormente con los comandos de kaddd . Por lo tanto, es razonable suponer que se completan los registros físicos, a menos que accidentalmente agotemos algún otro recurso utilizado al reemplazar la copia, que se comporta igual y tiene el mismo tamaño [14].

Encontramos confirmación adicional en el sitio web uops.info: dice que todas las variantes del comando kmov copy entre registros de máscara ocupan una microoperación ejecutada en el puerto p0 . Si hubiera una copia de reemplazo, no observaríamos actividad en los puertos.
De esto concluyo que los comandos de copia que usan registros de máscara [15] no se reemplazan.

Modismos de adicción


La mejor manera de anular el registro de propósito general en la arquitectura x86 es usar un idioma OR exclusivo (xor): xor reg , reg . Su acción se basa en el hecho de que comparar cualquier valor consigo mismo utilizando esta operación produce cero. Este comando es más corto (toma menos bytes) que el mov eax más obvio , 0 , y también más rápido, porque el procesador comprende que es un idioma de reinicio y realiza el cambio de nombre necesario de los registros [16], lo que elimina la necesidad de ALU y carga de puertos.

Además, este modismo elimina las dependencias de datos: generalmente el resultado del comando xor reg1 , reg2depende de los valores en los registros reg1 y reg2 , pero en el caso especial cuando reg1 y reg2 contienen el mismo valor, no hay dependencia, ya que para cualquier valor de entrada la salida será cero. Todos los procesadores x86 modernos reconocen este caso especial [17]. Lo mismo es cierto para aquellas versiones del lenguaje xor que usan registros SIMD, es decir, operaciones vpxor en enteros y vxorps y operaciones vxorpd en números reales.
Aquí, un lector curioso puede preguntar: ¿funciona este idioma con variantes similares del comando kxor ? Por ejemplo, ¿se consideraría el comando kxorb k1, k1, k1 [18] como un idioma de reinicio?
De hecho, estas son dos preguntas diferentes, ya que el efecto de usar un idioma de reinicio se compone de dos componentes:

  • Ejecución con retraso cero sin pasar por el módulo de ejecución ( eliminación de ejecución )
  • Eliminación de dependencia

Trataremos cada pregunta por separado.

Ejecución de reemplazo


Entonces, ¿se pueden reemplazar los comandos con xor, por ejemplo kxorb k1, k1, k1 , reasignando registros sin colocarlos en el módulo de ejecución?

No.

Ni siquiera tengo que hacer nada para demostrarlo: toda la información está en el sitio web uops.info, ya que realizaron una prueba de este tipo y mostraron que este comando se ejecuta con un retraso de 1 ciclo de reloj y toma una microoperación en el puerto p0 . De ello se deduce que los modismos xor reset para los registros de máscara no funcionan.

Eliminación de dependencia


Pero, ¿qué pasa si los modismos de reinicio con kxor aún eliminan las dependencias de datos, incluso si requieren colocación en el módulo de ejecución?

Aquí uops.info no nos ayudará. El comando kxor tiene un retraso de 1 ciclo de reloj y se ejecuta en un solo puerto ( p0 ), por lo tanto, existe una situación interesante (?) En la que la cadena de comandos kxor se ejecuta a la misma velocidad, independientemente de si hay dependencias entre ellos o no: ancho de banda la habilidad 1 comando / ciclo da la misma disminución de rendimiento que el retraso 1 comando / ciclo!

Nada, todavía tenemos un par de trucos en stock. La siguiente prueba nos ayudará a responder esta pregunta. Incrustar kxoren una cadena de comandos, en la que cada comando posterior depende del anterior, y el tiempo de ejecución total de esta cadena debe ser lo suficientemente grande como para formar un cuello de botella. Si el comando kxor no elimina la dependencia, el tiempo total de ejecución de la cadena será igual a la suma de los retrasos de sus comandos constituyentes. Si la dependencia desaparece, la cadena se divide en secuencias más cortas, cuyos retrasos se superponen, y luego la velocidad de su ejecución estará limitada por algún valor límite de rendimiento (asociado, por ejemplo, a la competencia por los puertos ). Esto podría mostrarse claramente usando el esquema, pero no soy fuerte en esto.

Todas estas pruebas se pueden encontrar en el banco de referencia uarchpero los puntos clave que daré a continuación.

Primero, mida el tiempo de copia estándar del registro general y viceversa:

kmovb k0, eax
kmovb eax, k0
;   127 

Un par de estos comandos se ejecuta [19] en 4 medidas. No se sabe exactamente cuánto tiempo pasa en cada una de ellas: 2 medidas o una medida, y 3 medidas en la otra [20]? Sin embargo, para nuestra tarea esto es irrelevante, porque estamos interesados ​​en el tiempo total de copiado de un lado a otro. Es de destacar que el ancho de banda de esta secuencia es 1 ciclo de reloj, que es 4 veces más rápido que el retraso, ya que cada comando se ejecuta en su propio puerto ( p5 y p0 , respectivamente). Esto significa que podemos separar el efecto del retraso del efecto del ancho de banda.

A continuación, en nuestra cadena, incluimos el comando kxor , que garantiza que no dará lugar a un restablecimiento de mayúsculas y minúsculas:

kmovb k0, eax
kxorb k0, k0, k1
kmovb eax, k0
;   127 

Como sabemos que kxorb tiene un retraso de 1 ciclo de reloj, el tiempo de ejecución total debería aumentar a 5 ciclos; esto es lo que muestra la prueba (se muestran los resultados de las dos primeras pruebas):

**   avx512 :  AVX512 **
                                       
mov  GP  kreg                    4.00         1.25
mov  GP  kreg   + 
kxorb                              5.00         1.57

Y finalmente, la prueba principal:
kmovb k0, eax
kxorb k0, k0, k0
kmovb eax, k0
;   127 

Esta vez usamos el comando kxorb con restablecimiento de mayúsculas y minúsculas: kxorb k0, k0, k0 . Si la dependencia del valor en el registro k0 desaparece, esto significará que el comando kmovb eax, k0 ya no depende del comando kmovb k0, eax anterior y que la cadena se ha roto y el tiempo total de ejecución debería disminuir.

Redoble de tambor ...

Obtuvimos las mismas 5.0 medidas, como en el ejemplo anterior:

**   avx512 :  AVX512 **
                                     
mov  GP  kreg                   4.00         1.25
mov  GP  kreg   + 
kxorb                             5.00         1.57
mov  GP  kreg   + 
kxorb                             5.00         1.57

La conclusión preliminar es la siguiente: el procesador no reconoce los modismos de reinicio si se aplican a los registros de máscara.

En conclusión, realizaremos otra prueba para asegurarnos de que nuestro razonamiento sea correcto: reemplazamos el comando kxor con el comando kmov , que, como saben, siempre elimina las dependencias:

kmovb k0, eax
kmovb k0, ecx
kmovb eax, k0
;   127 

La respuesta final se presenta a continuación. La última prueba es mucho más rápida: solo 2 ciclos de reloj y el cuello de botella es el puerto p5 (ambos comandos kmov k, r32 se ejecutan solo en este puerto):

**   avx512 :  AVX512 **
                                       
mov  GP  kreg                   4.00            1.25
mov  GP  kreg   + 
kxorb                             5.00            1.57
mov  GP  kreg   + 
kxorb                             5.00            1.57
mov  GP  kreg   + 
mov  GP                                  2.00            0.63

Resulta que nuestra suposición es correcta.

Resultados de reproducción


Puede reproducir todos los resultados presentados en este artículo usted mismo ejecutando el archivo ejecutable robsize en Linux o Windows (bajo WSL). También están disponibles en el repositorio , al igual que los scripts para recopilarlos y trazarlos.

recomendaciones


  • Los registros de máscara de arquitectura SKX se encuentran en un archivo de registro físico separado; 134 de ellos están diseñados para almacenar valores especulativos, el número total de registros de máscara es 142
  • Este número es comparable al tamaño de otros tipos de archivos de registro, así como al búfer ROB, y es lo suficientemente grande como para no experimentar una degradación del rendimiento cuando se trabaja con registros de máscara
  • Los comandos de copia con registros de máscara no se reemplazan
  • [21]

  1. k- kregs – -. , k – «» (m) «» (f).
  2. ( AVX-512 ), k0 – , , , . : k0 – , , , k, SIMD-, (, AVX-512). SIMD- k0 , .
  3. , -, 0, , . , , , , - - .
  4. « », kreg – ( k0, k1 ..), – «kreg» « » ( ).
  5. H. Wong, Measuring Reorder Buffer Capacity, May, 2013. [Online]. (. , « », 2013. -.) : blog.stuffedcow.net/2013/05/measuring-rob-capacity
  6. 100 300 . , - 2 50 100 , – 2,5 (, 2 5 ). TLB-/ .
  7. «» . , , . . , 29 104, , – , 200. , ( ) – , - ( ), .
  8. , , , (register alias table, RAT), . RAT , , , , . RAT , , .
  9. 60% 134 224, .. PRF ROB. , ROB 224 , , , [10] 60% , ROB. , - , 60% , ROB, .
  10. , , . , (, SIMD- ), . [2]
  11. , . , 2 2 SIMD- ( ), 4 .
  12. Physical Register Reclaim Table ( ) Post Retirement Reclaim Table ( ).
  13. – , « », .. , , . , . : , .
  14. , ( 7) , PRF ROB.
  15. , , , . , ( , , , – ).
  16. RAT , RAT , , .
  17. xor, , sub reg,reg sbb reg, reg. , reg 0 -1 ( ) . , reg, – , . .
  18. , : kxorb k1, k1, k1 , kxorb k1, k2, k2.
  19. , ,

    ./uarch-bench.sh --test-name=avx512/*. 

  20. uops.info kmov r32, k, kmov k, 32 <= 3. , 4 . 1 , , 3 .
  21. , xor, , , -, , . , : , .

All Articles