FEX tiene un problema entre manos, y se llama x86-TSO. La empresa escribe emuladores para sistemas x86, y el modelo de memoria que esos sistemas utilizan es estrictamente ordenado: cada almacenamiento es visible para todos los demás procesadores, cada carga ve todos los almacenamientos anteriores. Eso es lo opuesto a cómo funcionan las CPU ARM modernas, donde la memoria tiene un ordenamiento débil y los almacenamientos permanecen en las cachés hasta que se vacían.
El resultado es una batalla constante. FEX toma cada carga de memoria x86 y la convierte en una instrucción load-acquire de ARM. Cada almacenamiento x86 se convierte en una instrucción store-release de ARM. Eso le otorga al emulador la misma semántica de memoria que x86, pero es un truco costoso. Las CPU ARM no fueron construidas para que las instrucciones acquire y release sean la gran mayoría de lo que ejecutan, y FEX lo sabe.
La brecha del modelo de memoria
Un modelo de memoria es un conjunto de reglas sobre cómo se comportan los accesos a memoria en relación entre sí. x86-TSO es el extremo estricto del espectro. Un programador puede asumir que cuando ocurre un almacenamiento, este es visible para todos los procesadores del sistema. Una carga ve todos los almacenamientos anteriores a ella, lógicamente completados o al menos visibles. Los almacenamientos ordenan la visibilidad de las cargas, de ahí proviene el nombre.
El modelo de ARM es el otro extremo. Las cargas y almacenamientos regulares no son coherentes entre procesadores de forma predeterminada. Un almacenamiento no hace que su línea de caché sea visible para otros núcleos de inmediato. Un procesador que carga datos de memoria que otro procesador ha escrito no tiene garantizado ver el valor actualizado. Esa es la consistencia débil que permite a las CPU ARM ahorrar energía y funcionar de manera eficiente la mayor parte del tiempo.
Los dos modelos no son iguales. La consistencia y la atomicidad están relacionadas pero son separadas. ARM introdujo las instrucciones load-acquire y store-release para forzar el ordenamiento, y en términos de C++ estas se corresponden con memory_order_acquire y memory_order_release de std::atomic. ARM llama al modelo resultante Release Consistency sequentially consistent (RCsc). Las cargas acquire deben observarse secuencialmente sin reordenamiento. Los store-release deben cumplir la semántica «barrier-ordered-before». La instrucción de barrera de memoria anterior era costosa, y estas nuevas instrucciones la eliminaron.
ARMv8.0-a y el costo
FEX inicia su trayecto de emulación con ARMv8.0-a, donde la estrategia es simple: convertir todas las cargas de x86 en instrucciones acquire y todos los almacenamientos de x86 en instrucciones release. Eso le da a FEX la misma semántica de memoria que x86, pero la compañía admite que está siendo más estricto de lo necesario. Los microbenchmarks muestran el costo. Es sumamente costoso emular TSO de esta manera, y las CPU ARM no fueron diseñadas para que estas instrucciones poco frecuentes se conviertan de repente en la gran mayoría de lo que ejecutan.
La compañía probó esto con un microbenchmark que es benévolo con el hardware. Sin casos límite complicados, solo accediendo a memoria en el caso común. El gráfico cuenta varias historias. Las columnas Load y Store representan el número de rendimiento de referencia.
Clasificación de las instrucciones
- Estrategia de emulación de ARMv8.0-a: todas las cargas de x86 se convierten en instrucciones acquire y todos los almacenamientos de x86 se convierten en instrucciones release.
- La antigua instrucción de barrera de memoria: costosa y reemplazada por las nuevas instrucciones acquire y release.
- Las nuevas instrucciones load-acquire y store-release: más económicas que la antigua instrucción de barrera, pero aún costosas cuando se usan como la gran mayoría de la ejecución.
Lo que sabemos hasta ahora
- x86-TSO es un modelo de memoria estricto donde los almacenamientos son visibles para todos los procesadores y las cargas ven todos los almacenamientos anteriores.
- El modelo de ARM es débilmente consistente, lo que significa que los almacenamientos no son visibles para otros núcleos de inmediato.
- La emulación de ARMv8.0-a convierte las cargas de x86 en instrucciones acquire y los almacenamientos en instrucciones release.
- Los microbenchmarks muestran que el enfoque es sumamente costoso.
- La antigua instrucción de barrera de memoria fue reemplazada por load-acquire y store-release.
El veredicto
FEX tiene una solución funcional, pero es costosa. La empresa es consciente del costo y cuenta con microbenchmarks que lo demuestran.
La lección es que la emulación no es gratuita. Cada abstracción tiene un costo, y los modelos de memoria se encuentran entre las abstracciones más difíciles de salvar.
La historia vale la pena leerla para cualquiera que se interese por cómo se comporta la memoria dentro de una computadora.
Material de origen: “The scourge of x86 emulation”, fex-emu.com.
Recibe El Cuaderno.
Las mejores historias del día y cada veredicto nuevo, en español claro, en tu correo a las siete. Un correo al día, nada más.

