C variables volátiles y memoria caché
computer-science volatile (7)
Como dices, la caché es transparente para el programador. El sistema garantiza que siempre verá el último valor escrito si accede a un objeto a través de su dirección. La única "cosa" en la que puede incurrir si un valor obsoleto está en su caché es una penalización de tiempo de ejecución.
El caché está controlado por el hardware de caché de forma transparente para el procesador, por lo que si utilizamos variables volátiles en el programa C, ¿cómo se garantiza que mi programa lea datos cada vez desde la dirección de memoria real especificada pero no en caché?
Mi entendimiento es que
La palabra clave volátil le dice al compilador que las referencias de las variables no deben optimizarse y deben leerse como se programó en el código.
El caché está controlado por el hardware de caché de forma transparente, por lo tanto, cuando el procesador emite una dirección, no sabe si los datos provienen del caché o de la memoria.
Entonces, si tengo el requisito de tener que leer una dirección de memoria cada vez que lo necesito, ¿cómo puedo asegurarme de que no se refiera desde el caché sino desde la dirección requerida?
De alguna manera, estos dos conceptos no encajan bien. Por favor aclarar cómo se hace.
(Imaginando que tenemos una política de write-back en el caché (si es necesario para analizar el problema))
Gracias, Microkernel :)
De su pregunta hay un error de su parte.
Volatile
palabra clave Volatile
no está relacionada con el caché como usted describe.
Cuando se especifica la palabra clave volatile
para una variable, le da una pista al compilador para que no haga ciertas optimizaciones, ya que esta variable puede cambiar de otras partes del programa inesperadamente.
Lo que se quiere decir aquí, es que el compilador no debe reutilizar el valor ya cargado en un registro , sino acceder a la memoria nuevamente ya que no se garantiza que el valor en el registro sea el mismo que el valor almacenado en la memoria.
El resto relacionado con la memoria caché no está directamente relacionado con el programador.
Quiero decir que la sincronización de cualquier memoria caché de la CPU con la RAM es un tema completamente diferente.
Desarrollador de firmware aquí. Este es un problema estándar en la programación integrada, y uno que hace tropezar a muchos desarrolladores (incluso muy experimentados).
Supongo que está intentando acceder a un registro de hardware, y que el valor del registro puede cambiar con el tiempo (ya sea estado de interrupción, temporizador, indicaciones GPIO, etc.).
La palabra clave volatile
es solo parte de la solución, y en muchos casos puede no ser necesaria. Esto ocasiona que la variable vuelva a leerse de la memoria cada vez que se usa (en lugar de ser optimizada por el compilador o almacenada en un registro de procesador en múltiples usos), pero si la "memoria" que se lee es un registro de hardware real su código desconoce una ubicación almacenada en caché y no se ve afectada por la palabra clave volatile
. Si su función solo lee el registro una vez, es probable que pueda dejarlo volatile
, pero como regla general sugeriré que la mayoría de los registros de hardware deben definirse como volatile
.
El problema más grande es el almacenamiento en caché y la coherencia del caché. El enfoque más fácil aquí es asegurarse de que su registro esté en el espacio de direcciones no encasillado. Eso significa que cada vez que accede al registro, tiene la garantía de leer / escribir el registro de hardware real y no la memoria caché. Un enfoque más complejo, pero potencialmente mejor, es utilizar el espacio de direcciones en caché y hacer que su código fuerce manualmente las actualizaciones de caché para situaciones específicas como esta. Para ambos enfoques, cómo se logra esto depende de la arquitectura y está más allá del alcance de la pregunta. Podría involucrar MTRRs (para x86), MMU, modificaciones de tabla de páginas, etc.
Espero que ayude. Si me he perdido algo, hágamelo saber y ampliaré mi respuesta.
El uso de la palabra clave _Uncached puede ayudar en el sistema operativo integrado, como MQX
#define MEM_READ(addr) (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)
Mi sugerencia es marcar la página como no almacenada en caché por el administrador de memoria virtual.
En Windows, esto se hace configurando PAGE_NOCACHE
al llamar a VirtualProtect
.
Para un propósito algo diferente, las instrucciones SSE 2 tienen las instrucciones _mm_stream_xyz
para prevenir la contaminación de caché, aunque no creo que se apliquen a su caso aquí.
En cualquier caso, no hay una forma portátil de hacer lo que desea en C; tienes que usar la funcionalidad del sistema operativo.
Wikipedia tiene un artículo bastante bueno sobre MTRR (Registros de Rango de Tipo de Memoria) que se aplican a la familia de CPU x86.
Para resumirlo, comenzando con el Pentium Pro Intel (y AMD copiado) tenía estos registros MTR que podrían establecer atributos sin cache, escritura, combinación de escritura, protección contra escritura o reescritura en rangos de memoria.
Comenzando con el Pentium III pero, hasta donde yo sé, solo es realmente útil con los procesadores de 64 bits, honran a los MTRR pero pueden ser reemplazados por las tablas de atributos de página que permiten a la CPU establecer un tipo de memoria para cada página de memoria.
Un uso importante de los MTRRs que conozco es RAM de gráficos. Es mucho más eficiente marcarlo como combinación de escritura. Esto permite que la memoria caché almacene las escrituras y relaja todas las reglas de orden de escritura de memoria para permitir escrituras en ráfagas de muy alta velocidad en una tarjeta gráfica.
Pero para sus propósitos, usted desearía una configuración MTRR o PAT, ya sea sin almacenamiento o sin escritura.
volatile
se asegura de que los datos se lean cada vez que se necesitan sin molestarse con ningún caché entre la CPU y la memoria. Pero si necesita leer datos reales de la memoria y no datos en caché, tiene dos opciones:
- Haga un tablero donde dichos datos no se almacenen en caché. Este puede ser el caso si direcciona algún dispositivo de E / S,
- Use instrucciones específicas de la CPU que omitan la caché. Esto se usa cuando necesita borrar la memoria para activar posibles errores SEU.
Los detalles de la segunda opción dependen del sistema operativo y / o CPU.