• Aucun résultat trouvé

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.