windows - ¿Cómo habilitar excepciones de alineación para mi proceso en x64?
x64 assembly (3)
Esto funciona en la CPU Intel de 64 bits. Puede fallar en algunos AMD
pushfq
bts qword ptr [rsp], 12h ; reset AC bit of rflags
popfq
No funcionará de inmediato en las CPU de 32 bits, esto requerirá primero un controlador de kernel para cambiar el bit AM de CR0 y luego
pushfd
bts dword ptr [esp], 12h
popfd
Tengo curiosidad por ver si mi aplicación de 64 bits sufre de fallas de alineación.
Desde la alineación de datos de Windows en IPF, x86 y x64 :
En Windows, un programa de aplicación que genere un error de alineación generará una excepción,
EXCEPTION_DATATYPE_MISALIGNMENT
.
- En la arquitectura x64 , las excepciones de alineación están deshabilitadas de forma predeterminada y las reparaciones las realiza el hardware. La aplicación puede habilitar excepciones de alineación estableciendo un par de bits de registro , en cuyo caso las excepciones se generarán a menos que el usuario tenga el sistema operativo enmascarando las excepciones con
SEM_NOALIGNMENTFAULTEXCEPT
. (Para obtener más información, consulte el Manual del Programador de Arquitectura de AMD, Volumen 2: Programación del sistema ) .[Ed. énfasis mio]
En la arquitectura x86 , el sistema operativo no hace que la falla de alineación sea visible para la aplicación. En estas dos plataformas, también sufrirá una degradación del rendimiento en el fallo de alineación, pero será significativamente menos grave que en Itanium, porque el hardware realizará los accesos múltiples de la memoria para recuperar los datos no alineados.
En el Itanium , de forma predeterminada, el sistema operativo (OS) hará que esta excepción sea visible para la aplicación, y un controlador de terminación podría ser útil en estos casos. Si no configura un controlador, su programa se bloqueará o se bloqueará. En el Listado 3, proporcionamos un ejemplo que muestra cómo detectar la excepción EXCEPTION_DATATYPE_MISALIGNMENT.
Ignorando la dirección para consultar el Manual del Programador de Arquitectura de AMD , en su lugar, consultaré el Manual del Desarrollador de Software de Arquitecturas Intel 64 y IA-32
5.10.5 Comprobación de la alineación
Cuando la CPL es 3, se puede verificar la alineación de las referencias de la memoria configurando el indicador AM en el registro CR0 y el indicador AC en el registro EFLAGS. Las referencias de memoria no alineadas generan excepciones de alineación (#AC). El procesador no genera excepciones de alineación cuando opera en el nivel de privilegio 0, 1 o 2. Consulte la Tabla 6-7 para obtener una descripción de los requisitos de alineación cuando la verificación de alineación está habilitada.
Excelente. No estoy seguro de lo que eso significa, pero excelente.
Luego está también:
2.5 REGISTROS DE CONTROL
Los registros de control (CR0, CR1, CR2, CR3 y CR4; consulte la Figura 2-6) determinan el modo de funcionamiento del procesador y las características de la tarea que se está ejecutando actualmente. Estos registros son de 32 bits en todos los modos de 32 bits y en el modo de compatibilidad.
En el modo de 64 bits, los registros de control se expanden a 64 bits. Las instrucciones MOV CRn se utilizan para manipular los bits de registro. Los prefijos de tamaño operando para estas instrucciones se ignoran.
Los registros de control se resumen a continuación, y cada campo de control definido arquitectónicamente en estos registros de control se describe individualmente. En la Figura 2-6, el ancho del registro en modo de 64 bits se indica entre paréntesis (excepto para CR0). - CR0 : contiene indicadores de control del sistema que controlan el modo de operación y los estados del procesador
A.M
Máscara de alineación (bit 18 de CR0) : habilita la verificación de alineación automática cuando se establece; desactiva la verificación de alineación cuando está libre. La verificación de alineación se realiza solo cuando se establece el indicador AM, el indicador AC en el registro EFLAGS, CPL es 3 y el procesador está funcionando en modo protegido o virtual 8086.
Lo intenté
El lenguaje que estoy usando en realidad es Delphi, pero imagino que es un pseudocódigo agnóstico:
void UnmaskAlignmentExceptions()
{
asm
mov rax, cr0; //copy CR0 flags into RAX
or rax, 0x20000; //set bit 18 (AM)
mov cr0, rax; //copy flags back
}
La primera instrucción
mov rax, cr0;
falla con una excepción de Instrucción Privilegiada.
¿Cómo habilitar excepciones de alineación para mi proceso en x64?
PUSHF
Descubrí que el x86 tiene la instrucción:
-
PUSHF
,POPF
: Empuje /POPF
estallar los primeros 16 bits de EFLAGS en / fuera de la pila -
PUSHFD
,POPFD
: Empuje /POPFD
todos los 32 bits de EFLAGS en / fuera de la pila
Eso me llevó a la versión x64:
-
PUSHFQ
,POPFQ
: Presione / apague el RFLAGS quad dentro / fuera de la pila
(En el mundo de 64 bits, los EFLAGS
se denominan RFLAGS
).
Así que escribí:
void EnableAlignmentExceptions;
{
asm
PUSHFQ; //Push RFLAGS quadword onto the stack
POP RAX; //Pop them flags into RAX
OR RAX, $20000; //set bit 18 (AC=Alignment Check) of the flags
PUSH RAX; //Push the modified flags back onto the stack
POPFQ; //Pop the stack back into RFLAGS;
}
Y no se estrelló ni activó una excepción de protección. No tengo idea si hace lo que quiero.
Lectura de bonos
- Cómo detectar fallas de alineación de datos en x86 (también conocido como SIGBUS en Sparc) (pregunta no relacionada; x86 no x64, Ubunutu no Windows, gcc vs no)
Las aplicaciones que se ejecutan en x64 tienen acceso a un registro de bandera (a veces denominado EFLAGS ). El bit 18 en este registro permite que las aplicaciones obtengan excepciones cuando se producen errores de alineación. Entonces, en teoría, todo lo que un programa debe hacer para habilitar excepciones para errores de alineación es modificar el registro de indicadores.
sin embargo
Para que realmente funcione, el núcleo del sistema operativo debe configurar el bit 18 de cr0 para permitirlo. Y el sistema operativo de Windows no hace eso. Por qué no? ¿Quién sabe?
Las aplicaciones no pueden establecer valores en el registro de control. Solo el kernel puede hacer esto. Los controladores de dispositivo se ejecutan dentro del kernel, por lo que también pueden configurar esto.
Es posible hacer el desorden y tratar de hacer que esto funcione mediante la creación de un controlador de dispositivo (consulte http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx#199239 y los comentarios que siguen). Tenga en cuenta que esta publicación tiene más de una década, por lo que algunos de los enlaces están muertos.
También puede encontrar útil este comentario (y algunas de las otras respuestas en esta pregunta):
http://blogs.msdn.com/b/oldnewthing/archive/2004/07/27/198410.aspx#199239
En realidad, construimos una versión de NT con las excepciones de alineación activadas para x86 (puede hacerlo como lo mencionó Skywing).
Rápidamente lo apagamos, debido a la cantidad de aplicaciones que se rompieron :)
Como alternativa a AC para encontrar desaceleraciones debido a accesos no alineados, puede usar eventos de contador de rendimiento de hardware en CPU Intel para mem_inst_retired.split_loads
y mem_inst_retired.split_stores
para encontrar cargas / tiendas que se dividen en un límite de línea de caché.
perf record -c 10 -e mem_inst_retired.split_stores,mem_inst_retired.split_loads ./a.out
debería ser útil en Linux. -c 10
registra una muestra cada 10 eventos HW. Si su programa realiza una gran cantidad de accesos no alineados y solo quiere encontrar puntos de acceso reales, déjelo en el valor predeterminado. Pero -c 10
puede obtener datos útiles incluso en un pequeño binario que llama a printf una vez. Otras opciones de perf
, como -g
para grabar las funciones de los padres en cada trabajo de muestra como es habitual, y podrían ser útiles.
En Windows, use la herramienta que prefiera para mirar los contadores de rendimiento. VTune es popular.
Las CPU Intel modernas (familia P6 y más nuevas) no tienen ninguna penalización por desalineación dentro de una línea de caché . https://agner.org/optimize/ . De hecho, tales cargas / tiendas tienen incluso la garantía de ser atómicas (hasta 8 bytes), en las CPU de Intel. Por lo tanto, la CA es más estricta de lo necesario, pero ayudará a encontrar accesos potencialmente riesgosos que podrían ser divisiones de página o divisiones de línea de caché con datos alineados de manera diferente.
Las CPU de AMD pueden tener penalizaciones por cruzar un límite de 16 bytes dentro de una línea de caché de 64 bytes . No estoy familiarizado con los contadores de hardware disponibles allí. Tenga en cuenta que los perfiles en Intel HW no necesariamente encontrarán ralentizaciones que se produzcan en las CPU de AMD, si el acceso ofensivo nunca cruza un límite de línea de caché.
Consulte Cómo puedo comparar con precisión la velocidad de acceso no alineado en x86_64 para obtener algunos detalles sobre las penalizaciones, incluida mi prueba de latencia dividida en 4k y el rendimiento en Skylake.
Consulte también http://blog.stuffedcow.net/2014/01/x86-memory-disambiguation/ para conocer las posibles penalizaciones para la eficiencia de reenvío de la tienda para cargas / tiendas mal alineadas en Intel / AMD.
Ejecutar binarios normales con AC set no siempre es práctico . El código generado por el compilador podría optar por utilizar una carga o almacenamiento de 8 bytes no alineados para copiar varios miembros de estructura, o para almacenar algunos datos literales.
gcc -O3 -mtune=generic
(es decir, el valor predeterminado con la optimización habilitada) asume que las divisiones de línea de caché son lo suficientemente baratas como para que valga la pena el riesgo de usar accesos no alineados en lugar de múltiples accesos estrechos como lo hace la fuente. Las divisiones de páginas se hicieron mucho más baratas en Skylake, de ~ 100 a 150 ciclos en Haswell a 10 ciclos en Skylake (casi la misma penalización que las divisiones CL), porque aparentemente Intel descubrió que eran menos raras de lo que pensaban anteriormente.
Muchas funciones de biblioteca optimizadas (como memcpy
) utilizan accesos enteros no alineados. por ejemplo, el memcpy
de glibc, para una copia de 6 bytes, haría 2 cargas superpuestas de 4 bytes desde el inicio / final del búfer, luego 2 tiendas superpuestas. (No tiene un caso especial para exactamente 6 bytes para hacer una palabra dword +, solo incrementando los poderes de 2). Este comentario en la fuente explica sus estrategias.
Así que incluso si su sistema operativo le permitiera habilitar la CA, es posible que necesite una versión especial de las bibliotecas para no activar la CA en todo el lugar para cosas como pequeñas memcpy
.
La alineación cuando se realiza un bucle secuencialmente sobre una matriz realmente importa para AVX512, donde un vector es del mismo ancho que una línea de caché. Si sus punteros están desalineados, cada acceso es una división de línea de caché, no solo todos los demás con AVX2. La alineación siempre es mejor, pero para muchos algoritmos con una cantidad decente de computación combinada con acceso a la memoria, solo hace una diferencia significativa con AVX512.
Los accesos desalineados dispersos que cruzan un límite de línea de caché esencialmente tienen el doble de huella de caché al tocar ambas líneas, si las líneas no se tocan de otro modo.