c++ - visual - ¿Qué es "alineación de pila"?
visual studio code para c (4)
Algunas arquitecturas de CPU requieren una alineación específica de varios tipos de datos y lanzarán excepciones si no respeta esta regla. En modo estándar, x86 no requiere esto para los tipos de datos básicos, pero puede sufrir penalizaciones de rendimiento (consulte www.agner.org para obtener consejos de optimización de bajo nivel).
Sin embargo, el procesamiento de audio / video del SSE instrucciones SSE (a menudo utilizado para alto rendimiento) tiene estrictos requisitos de alineación y generará excepciones si intenta usarlo en datos no alineados (a menos que utilice, en algunos procesadores, versiones desalineadas mucho más lentas) )
Su problema es probablemente que un compilador espera que el llamante mantenga la pila alineada, mientras que el otro espera que el destinatario alinee la pila cuando sea necesario.
EDITAR : en cuanto a por qué ocurre la excepción, una rutina en el DLL probablemente quiera usar instrucciones SSE en algunos datos de pila temporales, y falla porque los dos compiladores diferentes no están de acuerdo con las convenciones de llamada.
¿Qué es la alineación de pila? ¿Por qué se usa? ¿Puede ser controlado por la configuración del compilador?
Los detalles de esta pregunta están tomados de un problema al tratar de usar las librerías ffmpeg con msvc, sin embargo, lo que realmente me interesa es una explicación de lo que es "alineación de pila".
Los detalles:
- Cuando ejecuto mi programa msvc cumplido, que enlaza con avcodec, aparece el siguiente error: "El compilador no alineó las variables de la pila. Libavcodec ha sido mal compilado", seguido de un bloqueo en avcodec.dll.
- avcodec.dll no se compiló con msvc, por lo que no puedo ver lo que sucede dentro.
- Al ejecutar ffmpeg.exe y usar el mismo avcodec.dll todo funciona bien.
- ffmpeg.exe no se compiló con msvc, se cumplió con gcc / mingw (igual que avcodec.dll)
Gracias,
Dan
Alineación de variables en la memoria (un breve historial).
En el pasado, las computadoras tenían un bus de datos de 8 bits. Esto significa que cada ciclo de reloj puede procesar 8 bits de información. Lo cual estaba bien entonces.
Luego vinieron las computadoras de 16 bits. Debido a la compatibilidad descendente y otros problemas, se mantuvo el byte de 8 bits y se introdujo la palabra de 16 bits. Cada palabra tiene 2 bytes. Y cada ciclo de reloj podría procesarse 16 bits de información. Pero esto planteó un pequeño problema.
Miremos un mapa de memoria:
+----+
|0000|
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |
En cada dirección hay un byte al que se puede acceder de forma individual. Pero las palabras solo se pueden buscar en direcciones pares. Entonces, si leemos una palabra en 0000, leemos los bytes en 0000 y 0001. Pero si queremos leer la palabra en la posición 0001, necesitamos dos accesos de lectura. Primero 0000,0001 y luego 0002,0003 y solo guardamos 0001,0002.
Por supuesto, esto tomó algo de tiempo extra y eso no fue apreciado. Entonces es por eso que inventaron la alineación. Así que almacenamos variables de palabras en límites de palabras y variables de bytes en límites de bytes.
Por ejemplo, si tenemos una estructura con un campo de bytes (B) y un campo de palabras (W) (y un compilador muy ingenuo), obtenemos lo siguiente:
+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+
Lo cual no es divertido Pero cuando usamos la alineación de palabras encontramos:
+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+
Aquí la memoria se sacrifica por la velocidad de acceso.
Puede imaginarse que al usar la palabra doble (4 bytes) o la palabra cuadrada (8 bytes) esto es aún más importante. Es por eso que con la mayoría de los compiladores modernos puede elegir qué alineación está usando al compilar el programa.
Hasta donde yo sé, los compiladores generalmente no alinean las variables que están en la pila. La biblioteca puede depender de algún conjunto de opciones de compilación que no sea compatible con su compilador. La solución normal es declarar las variables que deben alinearse como estáticas, pero si va a hacer esto en el código de otras personas, querrá asegurarse de que las variables en cuestión se inicialicen más adelante en la función en lugar de en la declaracion.
// Some compilers won''t align this as it''s on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;
Alternativamente, encuentre un interruptor de compilación que alinee las variables en la pila. Obviamente, la sintaxis de alineación "__declspec" que he usado aquí puede no ser lo que usa tu compilador.
IIRC, la alineación de la pila es cuando las variables se colocan en la pila "alineadas" a un número particular de bytes. Entonces, si está utilizando una alineación de pila de 16 bits, cada variable de la pila comenzará desde un byte que es un múltiplo de 2 bytes desde el puntero de pila actual dentro de una función.
Esto significa que si usa una variable que es <2 bytes, como un char (1 byte), habrá 8 bits de "relleno" sin usar entre este y la siguiente variable. Esto permite ciertas optimizaciones con suposiciones basadas en ubicaciones variables.
Al llamar a funciones, un método para pasar argumentos a la siguiente función es colocarlos en la pila (en lugar de colocarlos directamente en los registros). Si la alineación se está utilizando o no aquí es importante, ya que la función de llamada coloca las variables en la pila, para ser leídas por la función de llamada usando compensaciones. Si la función de llamada alinea las variables y la función llamada espera que no estén alineadas, entonces la función llamada no podrá encontrarlas.
Parece que el código msvc compilado no está de acuerdo con la alineación de variables. Intente compilar con todas las optimizaciones desactivadas.