C variables volatiles et mémoire cache

Le cache est contrôlé de manière transparente par le processeur vers le processeur. Par conséquent, si nous utilisons des variables volatiles dans le programme C, comment peut-on garantir que mon programme lit les données à chaque fois à partir de l’adresse mémoire spécifiée, mais pas de la mémoire cache.

Ma compréhension est que,

  1. Le mot clé volatile indique au compilateur que les références de variable ne doivent pas être optimisées et doivent être lues comme programmées dans le code.

  2. Le cache est contrôlé de manière transparente par le matériel de cache. Ainsi, lorsque le processeur envoie une adresse, il ne sait pas si les données proviennent du cache ou de la mémoire.

Donc, si j’ai besoin de lire une adresse mémoire chaque fois que cela est nécessaire, comment puis-je être sûr qu’elle ne soit pas référencée depuis le cache mais depuis l’adresse requirejse?

Certains comment, ces deux concepts ne vont pas bien ensemble. S’il vous plaît clarifier comment c’est fait.

(Imaginer que nous avons une politique de réécriture en cache (si nécessaire pour parsingr le problème))

Merci, Microkernel 🙂

Développeur de micrologiciel ici. Il s’agit d’un problème standard en programmation intégrée, qui pose problème à de nombreux développeurs (même très expérimentés).

Mon hypothèse est que vous essayez d’accéder à un registre matériel et que sa valeur peut changer dans le temps (qu’il s’agisse du statut d’interruption, de la timer, des indications GPIO, etc.).

Le mot-clé volatile n’est qu’un élément de la solution et, dans de nombreux cas, peut ne pas être nécessaire. La variable est relue de la mémoire chaque fois qu’elle est utilisée (au lieu d’être optimisée par le compilateur ou stockée dans un registre de processeur pour plusieurs utilisations), mais si la “mémoire” en cours de lecture est un registre matériel réel. par rapport à un emplacement mis en cache est inconnu de votre code et n’est pas affecté par le mot clé volatile . Si votre fonction ne lit le registre qu’une seule fois, vous pouvez probablement laisser volatile , mais en règle générale, je suggérerai que la plupart des registres matériels doivent être définis comme volatile .

Le problème le plus important est la mise en cache et la cohérence du cache. La méthode la plus simple consiste à s’assurer que votre registre est dans un espace adresse non mis en cache. Cela signifie que chaque fois que vous accédez au registre, vous avez la garantie de lire / écrire le registre matériel réel et non la mémoire cache. Une approche plus complexe mais potentiellement plus performante consiste à utiliser un espace d’adressage mis en cache et à forcer votre code à forcer manuellement les mises à jour de cache dans des situations spécifiques comme celle-ci. Pour les deux approches, la manière dont cela est réalisé dépend de l’architecture et dépasse le cadre de la question. Il peut s’agir de MTRR (pour x86), de MMU, de modifications de table de page, etc.

J’espère que cela pourra aider. Si j’ai raté quelque chose, faites le moi savoir et je développerai ma réponse.

Ma suggestion est de marquer la page comme non mise en cache par le gestionnaire de mémoire virtuelle.
Sous Windows, cela se fait en paramétrant PAGE_NOCACHE lors de l’appel de VirtualProtect .

Dans un but quelque peu différent, les instructions SSE 2 utilisent les instructions _mm_stream_xyz pour empêcher la pollution du cache, bien que je ne pense pas qu’elles s’appliquent à votre cas ici.

Dans les deux cas, il n’existe aucun moyen portable de faire ce que vous voulez en C; vous devez utiliser la fonctionnalité du système d’exploitation.

De votre question, il y a une idée fausse de votre part.
Volatile mot clé Volatile n’est pas lié au cache tel que vous le décrivez.

Lorsque le mot clé volatile est spécifié pour une variable, il indique au compilateur de ne pas effectuer certaines optimisations car cette variable peut être modifiée de manière inattendue par rapport à d’autres parties du programme.

Ce que l’on entend ici, c’est que le compilateur ne doit pas réutiliser la valeur déjà chargée dans un registre , mais accéder à nouveau à la mémoire car il n’est pas garanti que la valeur de registre est identique à la valeur stockée en mémoire.

Le rest concernant la mémoire cache n’est pas directement lié au programmeur.

Je veux dire que la synchronisation de toute mémoire cache de la CPU avec la RAM est un sujet totalement différent.

Wikipedia a un très bon article sur les MTRR (registres de types de mémoire) qui s’appliquent à la famille de processeurs x86.

En résumé, à partir du Pentium Pro Intel (et copié par AMD), ces registres MTR permettaient de définir des atsortingbuts non mis en cache, écriture-combinaison, combinaison-écriture, protection contre l’écriture ou écriture sur des plages de mémoire.

À commencer par le Pentium III mais, à ma connaissance, utile uniquement avec les processeurs 64 bits, ils respectent les MTRR, mais ils peuvent être remplacés par les tables d’atsortingbuts de page qui permettent à la CPU de définir un type de mémoire pour chaque page de mémoire.

Une des principales utilisations des MTRR que je connaisse est la mémoire vive graphique. Il est beaucoup plus efficace de le marquer comme combinaison d’écriture. Cela permet au cache de stocker les écritures et d’assouplir toutes les règles de classement des écritures en mémoire afin de permettre des écritures en rafale à très grande vitesse sur une carte graphique.

Mais pour vos besoins, vous voudriez soit un paramètre MTRR, soit un paramètre PAT non mis en cache ou inscriptible.

L’utilisation du mot clé _Uncached peut aider les systèmes d’exploitation intégrés, tels que MQX.

 #define MEM_READ(addr) (*((volatile _Uncached unsigned int *)(addr))) #define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data) 

Comme vous le dites, le cache est transparent pour le programmeur. Le système garantit que vous voyez toujours la dernière valeur écrite si vous accédez à un object via son adresse. La “seule” chose que vous pouvez subir si une valeur obsolète se trouve dans votre cache est une pénalité d’exécution.

volatile s’assure que les données sont lues à chaque fois que nécessaire, sans aucun problème de cache entre le processeur et la mémoire. Mais si vous devez lire les données réelles dans la mémoire et non dans les données en cache, vous avez deux options:

  • Créez un tableau où les données ne sont pas mises en cache. Cela peut déjà être le cas si vous adressez un périphérique d’E / S,
  • Utilisez des instructions spécifiques du processeur qui contournent le cache. Ceci est utilisé lorsque vous devez nettoyer la mémoire pour activer les erreurs SEU possibles.

Les détails de la deuxième option dépendent du système d’exploitation et / ou de la CPU.