Cœur 1
registres
Cœur 2
registres L1 Data L1 Instr. L1 Data L1 Instr.
L2 M´emoire centrale Lent
Rapide Faiblecapacit´e
Large capacit´e
Figure 2.6 – Exemple de hiérarchie mémoire avec un cache L1 de données et un cache
L1 d’instructions par cœur, et un cache L2 partagé.
2.3 Fonctionnement d’un cache
Une mémoire cache garde en mémoire les informations récemment utilisées car il est fort
probable que ces informations soient à nouveau utiles dans un futur proche. Tous les caches
ne sont pas identiques et il existe plusieurs paramètres importants pour un cache. L’objectif
de cette partie est d’expliquer le fonctionnement interne des caches et de présenter les
paramètres qui permettent de les caractériser.
2.3.1 Associativité
Avant toute chose, il est important de préciser qu’un cache ne manipule pas les informations
octet par octet mais par groupe de plusieurs octets, appelé ligne ou bloc, et dont la taille
varie d’une architecture à une autre. Ceci permet en particulier de limiter la quantité
d’informations nécessaires à la gestion du cache. Un cache est ainsi constitué d’un ensemble
de lignes.
Les informations contenues dans la mémoire centrale sont donc considérées sous la forme
de lignes et ces lignes seront recopiées dans les emplacements du cache. Pour des raisons de
clarté, nous supposerons par la suite que l’adressage se fait à l’octet, ce qui ne change pas
les explications. L’adresse mémoire d’un octet est découpée en deux parties : le numéro de
bloc qui permet d’identifier la ligne, et le numéro de l’octet qui permet d’identifier l’octet
au sein d’une ligne. La figure 2.7 montre l’exemple du découpage d’une adresse 32 bits
pour identifier le bloc de 64 octets et l’octet au sein de ce bloc.
L’emplacement mémoire à l’intérieur du cache où sera recopiée la ligne dépend de la
stra-tégie du cache. Nous verrons ci-après le fonctionnement d’un cache direct où chaque ligne
mémoire dispose d’un emplacement unique dans le cache, le fonctionnement d’un cache
associatif qui décide de l’emplacement à utiliser à l’aide d’un algorithme de remplacement
et enfin nous aborderons le cas des caches associatifs par ensembles qui sont un compromis
entre ces deux approches.
Node bloc Nooctet 31 5 0 Bloc 0 Bloc 1 Bloc 2 Bloc 3 Bloc 4 Bloc 5 Bloc 6 Bloc 7 ... ... M´emoire centrale Adresse m´emoire d’un octet :
Figure 2.7 – Exemple de découpage d’une adresse mémoire pour identifier, dans la
mé-moire, un octet au sein d’un bloc de 64 octets (2
6).
a) Cache direct (direct mapping)
Dans le cas d’un cache utilisant une correspondance directe, chaque bloc de la mémoire
est associé à un emplacement unique dans le cache. Cet emplacement est typiquement
déterminé par l’indice formé par les bits de poids faible du numéro de bloc. Les bits
restants du numéro de bloc (bits de poids fort) sont mémorisés dans le cache afin de
conserver l’adresse du bloc actuellement présent dans le cache. Ces bits sont appelés letag
de la ligne.
La figure 2.8 illustre ce découpage. Afin de mieux comprendre le fonctionnement d’un
cache direct, prenons l’exemple de la lecture d’un octet. Les 13 bits d’indice permettent
d’identifier l’emplacement du cache où la ligne accédée devrait être, si elle est présente
dans le cache. Ensuite, les bits de poids fort (Tag) sont comparés pour vérifier que la ligne
présente dans le cache correspond à celle à laquelle on souhaite accéder. Si c’est le cas,
l’octet est lu dans le cache en utilisant le numéro de l’octet. Sinon le bloc sera chargé
depuis la mémoire et remplacera la ligne qui était présente au même emplacement.
Tag Indice Nooctet
31 18 5 0
Tag Lignes de 64 octets ...
Octet
8192 (=213) lignes
Figure 2.8 – Cache direct de 512 Kio (8192 lignes de 64 octets).
Le principal inconvénient de cette méthode est qu’une ligne sera retirée du cache si un
accès est réalisé à une autre adresse qui possède le même emplacement dans le cache.
Le risque est donc d’avoir un programme qui manipule en boucle deux valeurs qui vont
s’exclure alternativement du cache.
L’intérêt de cette méthode est essentiellement la simplicité de sa mise en œuvre et sa
vitesse d’exécution. En effet, on sait rapidement si une ligne est présente dans le cache et
on sait également quelle ligne remplacer si besoin.
2.3. Fonctionnement d’un cache 47
b) Cache associatif (fully associative)
De manière alternative, dans un cache associatif, une ligne peut potentiellement utiliser
n’importe quel emplacement du cache. C’est un algorithme complémentaire qui va décider,
pour chacune d’entre elles, au moment de leur recopie dans le cache, quel emplacement
dans le cache elles doivent occuper.
L’inconvénient majeur de cette approche est la nécessité, à chaque accès au cache, de
comparer le numéro du bloc demandé avec celui de l’ensemble des lignes présentes dans
le cache afin de vérifier si la ligne est présente dans le cache. Au contraire, cette méthode
permet de garder dans le cache les lignes qui ont le plus de chance d’être accédées à
nouveau et donc de réduire le nombre de défauts de cache.
Lors d’un défaut de cache, l’algorithme de remplacement est appelé afin de déterminer
l’emplacement du cache qui doit être utilisé pour accueillir la nouvelle ligne. Il existe tout
un ensemble d’algorithmes pour ce faire tels que LRU (Least Recently Used), LFU (Least
Frequently Used), FIFO, aléatoire, etc. La complexité de ces algorithmes influe sur les
temps d’accès du cache. Les politiques les plus courantes sont LRU et ses approximations.
Un cache LRU conserve les dernières lignes utilisées. Ainsi, lors d’un défaut de cache, la
nouvelle ligne remplacera la ligne présente dans le cache dont la date du dernier accès est
la plus ancienne. Un cache associatif provoque généralement moins de défauts de cache
que la méthode par accès direct, mais elle est aussi plus complexe (algorithmiquement) et
donc plus consommatrice en temps. Dès que le nombre de lignes devient trop grand, cette
méthode est à écarter.
c) Cache associatif par ensemble à N voies (N-way set associative)
La correspondance associative par ensemble à N voies est une solution intermédiaire aux
caches directs et associatifs qui se veut être un bon compromis entre ces deux approches.
Cette solution consiste à adresser de manière directe un ensemble d’emplacements du cache
puis d’utiliser le fonctionnement d’un cache associatif sur ce sous-ensemble du cache. Ainsi,
on limite les inconvénients du cache associatif par un nombre réduit de lignes à comparer
tout en gardant une partie de ses avantages. C’est donc un compromis entre un cache
rapide (direct) et un cache qui fait moins de défauts de cache (associatif).
Un cache associatif par ensemble à N voies est un cache où chaque ensemble contient N
emplacements. On dit aussi que son associativité est de N. Un cache direct est un cas
particulier où l’associativité est de 1, et un cache entièrement associatif est un cache où
l’associativité est égale à la capacité en nombre de lignes du cache.
La figure 2.9 montre le fonctionnement d’un cache associatif d’associativité 8 utilisant la
politique de remplacement LRU. L’indice de la ligne sert à identifier l’ensemble du cache
mais il est alors nécessaire de comparer le tag des 8 lignes de l’ensemble pour déterminer
si la ligne est présente ou non. Si la ligne n’est pas présente, la ligne la plus anciennement
utilisée sera remplacée.
Il est fréquent de voir des caches avec une correspondance directe ou avec une faible
associativité (2 ou 4 lignes) pour les caches L1 dont on exige une grande vitesse. Alors
qu’au contraire, pour les caches de niveau supérieur, l’associativité peut monter entre 2 et
16. Expérimentalement, lorsque l’associativité est supérieure à 8 ou 16, les résultats sont
très proches de ce qui peut être obtenu avec un cache entièrement associatif [HS89]. Ainsi,
les caches entièrement associatifs sont peu utilisés en pratique.
Tag Indice Nooctet
31 15 5 0
Tag Line Tag Line Tag Line Tag Line Tag Line Tag Line Tag Line Tag Line
8-ways LRU
1024 (=210) ensembles
Figure2.9 – Cache associatif 8-ways de 512 Kio (8192 lignes de 64 octets). 10 bits servent
à identifier un ensemble parmi les 1024.
2.3.2 Protocoles de cohérence
Dans le cas d’architectures multiprocesseurs, il est nécessaire d’avoir un protocole de
cohé-rence qui évite à un processeur de manipuler des données obsolètes. Un bloc peut posséder
de nombreuses copies : potentiellement un par cache en plus de la mémoire centrale.
Lors-qu’un bloc est modifié, toutes ses copies doivent être actualisées. De plus, l’ordre temporel
des modifications doit être le même pour tous les processeurs.
Pour cela, il existe de nombreux protocoles dont les plus répandus semblent être MESI
et MOESI. Ces deux protocoles mettent en œuvre une politique d’écriture de type
Write-back, c’est-à-dire que les écritures sont différées (au moment où la ligne sera évincée du
cache). Contrairement à une stratégie de type Write-through où les écritures se font
im-médiatement en mémoire et sur les copies en cache.
Dans le document
Étude et évaluation des politiques d'ordonnancement temps réel multiprocesseur
(Page 56-59)