σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyFeuilles de routeParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Feuilles de routeParcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyFeuilles de routeParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Feuilles de routeParcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Cache locality›Module 1 · Les caches de zéro›Sujet 1.3·~55 minIntermédiaire

Associativité et défauts de conflit

Position dans le parcoursModule 1 sur 3 · Sujet 3 sur 4
1.3 Associativité et défauts de conflit
—Score du lab— / 100XP total—Série—

6 min de lecture4 exemples de code

  1. 01La faiblesse d'un seul emplacement
  2. 02Ensembles et voies
  3. 03Qui est évincé : LRU
  4. 04Des lignes espacées d'une puissance de deux
  5. 05Ce que vous allez construire

Dans un cache à un emplacement par ligne, deux lignes très utilisées qui partagent un emplacement s'évincent encore et encore. Donnez plusieurs voies à chaque emplacement et le problème disparaît en grande partie, sauf si les lignes de votre table sont espacées d'exactement 4096 octets. Ce cours construit un cache associatif par ensembles et montre comment une taille de ligne puissance de deux peut faire échouer 100 % des lectures.

01

La faiblesse d'un seul emplacement

Dans le cache à correspondance directe du sujet précédent, chaque ligne de mémoire a exactement un emplacement. Si votre programme utilise sans cesse deux lignes qui correspondent au même emplacement, disons la ligne 0 et la ligne 8 dans un cache de 8 emplacements, chaque lecture de l'une évince l'autre, alors que les sept autres emplacements sont peut-être vides. La taille du cache n'est pas en cause : les lignes se disputent une place trop petite. Un défaut de ce genre s'appelle un défaut de conflit.

02

Ensembles et voies

La solution est de regrouper les emplacements. Les emplacements sont réunis en ensembles, et chaque emplacement d'un ensemble est une voie. Une ligne de mémoire appartient à un ensemble, trouvé par le même reste qu'avant, mais elle peut se placer dans n'importe quelle voie de cet ensemble. Deux lignes en collision partagent maintenant un ensemble qui a de la place pour les deux. Le nombre d'ensembles découle des tailles :

sets = cache size ÷ (line size × ways)(1)un cache de 32 Ko à lignes de 64 octets et 8 voies a 32768 ÷ (64 × 8) = 64 ensembles

Une adresse se découpe maintenant en trois parties. Le déplacement est l'octet dans la ligne. L'indice d'ensemble dit à quel ensemble la ligne appartient. L'étiquette est le reste du numéro de ligne, et c'est ce que le cache compare pour savoir si une voie contient cette ligne. La fonction ci-dessous découpe une adresse pour un cache à lignes de 64 octets et 64 ensembles.

main.pyPython
def split_address(address, line_bytes, n_sets):    line = address // line_bytes    return line // n_sets, line % n_sets, address % line_bytes1print(split_address(74565, 64, 64))
(18, 13, 5)
  1. 1Étiquette, indice d'ensemble et déplacement : chacun est une division entière ou un reste de l'adresse.

L'adresse 74565 est l'octet 5 de la ligne 1165. La ligne 1165 va dans l'ensemble 13 (1165 modulo 64), et 18 est ce qui reste du numéro de ligne, l'étiquette. Toute ligne dont l'ensemble est 13 se disputera les huit voies de cet ensemble, quelle que soit son étiquette.

03

Qui est évincé : LRU

Quand un ensemble est plein et qu'une nouvelle ligne arrive, l'une des lignes doit partir. La règle habituelle est LRU : évincer la ligne utilisée le moins récemment. On peut garder chaque ensemble comme une liste Python ordonnée de la moins récemment utilisée à la plus récemment utilisée.

  1. 1
    Trouver l'ensembleLe numéro de ligne modulo le nombre d'ensembles.
  2. 2
    Regarder dans chaque voieEn cas de succès, déplacez la ligne vers le bout des plus récemment utilisées.
  3. 3
    Ajouter en cas de défautPlacez la nouvelle ligne au bout des plus récemment utilisées.
  4. 4
    Éliminer la plus ancienneSi l'ensemble contient plus de lignes que de voies, retirez la première.

Voici le cache complet. L'exécution en bas lit deux lignes, aux adresses 0 et 256, qui entrent en collision dans un cache de 256 octets à lignes de 64 octets, d'abord avec une voie puis avec deux.

main.pyPython
class SetAssocCache:    def __init__(self, size_bytes, line_bytes, ways):        self.line_bytes = line_bytes        self.ways = ways        self.n_sets = size_bytes // (line_bytes * ways)        self.sets = [[] for _ in range(self.n_sets)]        self.hits = 0        self.misses = 0    def access(self, address):        line = address // self.line_bytes        lines = self.sets[line % self.n_sets]        if line in lines:            lines.remove(line)            lines.append(line)1            self.hits += 1            return True        self.misses += 1        lines.append(line)        if len(lines) > self.ways:            lines.pop(0)2        return Falsefor ways in (1, 2):    cache = SetAssocCache(size_bytes=4 * 64, line_bytes=64, ways=ways)    flags = [cache.access(a) for a in [0, 256, 0, 256, 0, 256]]    print(ways, "way:", flags)
1 way: [False, False, False, False, False, False]
2 way: [False, False, True, True, True, True]
  1. 1Déplacer une ligne qui réussit à la fin garde la liste ordonnée : la première entrée est toujours la moins récemment utilisée.
  2. 2L'ensemble plein perd sa première entrée, la ligne la moins récemment utilisée.

Avec une voie, les deux lignes s'évincent et les six lectures échouent. Avec deux voies, les deux lignes tiennent dans leur ensemble : après les deux premiers défauts à froid, chaque lecture réussit. Ajouter une voie a supprimé tous les défauts de conflit de ce motif.

Pourquoi pas le plus de voies possible ?

Chaque voie ajoute une comparaison d'étiquette à chaque lecture, ce qui coûte du temps et de l'énergie. Les caches de niveau 1 ont souvent 8 voies et les plus grands 12 à 16, car au-delà chaque voie en plus supprime très peu de défauts.

04

Des lignes espacées d'une puissance de deux

Voici le piège. Une table de 512 colonnes float64 a des lignes d'exactement 4096 octets. Lire une colonne revient à lire les adresses 0, 4096, 8192 et ainsi de suite. Dans notre cache, l'indice d'ensemble est le numéro de ligne modulo 64, et 4096 octets font exactement 64 lignes : toutes ces adresses ont donc le même indice d'ensemble. Le code affiche l'ensemble des six premières lignes pour une taille de ligne de 4096 octets et pour 4160 octets, soit 4096 plus une ligne de plus.

main.pyPython
n_sets = 64for row_bytes in (4096, 4160):    sets = [(i * row_bytes // 64) % n_sets for i in range(6)]    print(row_bytes, "->", sets)
4096 -> [0, 0, 0, 0, 0, 0]
4160 -> [0, 1, 2, 3, 4, 5]

Avec des lignes de 4096 octets, toutes les clôtures d'une colonne se disputent l'ensemble 0. Avec des lignes de 4160 octets, elles parcourent les ensembles 0, 1, 2, 3 et ainsi de suite, en utilisant tout le cache. Le code suivant lit 96 clôtures deux fois et mesure le taux de défauts pour 1, 2, 4 et 8 voies.

main.pyPython
def column_trace(n_rows, row_bytes, passes):    return [i * row_bytes for i in range(n_rows)] * passesdef miss_rates_by_ways(trace, size_bytes, line_bytes, ways_list):    rates = {}    for ways in ways_list:        cache = SetAssocCache(size_bytes, line_bytes, ways)        for address in trace:            cache.access(address)        rates[ways] = cache.misses / len(trace)    return ratesfor row_bytes in (4096, 4160):    print(row_bytes, miss_rates_by_ways(column_trace(96, row_bytes, 2), 32768, 64, [1, 2, 4, 8]))
4096 {1: 1.0, 2: 1.0, 4: 1.0, 8: 1.0}
4160 {1: 0.5, 2: 0.5, 4: 0.5, 8: 0.5}

La table remplie ne rate qu'au premier passage : 0,5 de toutes les lectures, quel que soit le nombre de voies. La table alignée rate à chaque lecture, même avec 8 voies, car 96 lignes ont besoin de 96 places et l'ensemble en a 8. Pire, LRU évince toujours la ligne que vous lirez ensuite dans un motif cyclique, donc le second passage ne trouve rien. Plus de voies n'aideraient qu'en atteignant le nombre de lignes.

Les puissances de deux sont les pires tailles de ligne

Une taille de ligne multiple de l'étendue d'un ensemble (ici 64 lignes, 4096 octets) met toute une colonne dans un seul ensemble. Les matrices et tables de 256, 512 ou 1024 colonnes float64 sont les victimes classiques. Remplir chaque ligne d'une ligne de cache de plus, comme 4160, coûte 1,6 % de mémoire et supprime le problème.

Ce que disent les vraies données96 premières clôtures de SPY, lues deux fois
Taux de défauts, lignes espacées de 4096 octets100%8 voies, 64 ensembles : chaque lecture échoue
Taux de défauts, lignes espacées de 4160 octets50%seul le premier passage échoue
Taux de défauts de la table de 4096 octets selon le nombre de voies, en %
1 voie100
2 voies100
4 voies100
8 voies100
05

Ce que vous allez construire

Le labo suit le cours : le découpage de l'adresse, le cache LRU, les deux sortes de traces, la comparaison selon le nombre de voies et l'étude sur la vraie table SPY.

  • Fonction · questionS'appuie sur
  • split_address(address, line_bytes, n_sets)Quelle étiquette, quel ensemble et quel déplacement une adresse a-t-elle ?La division entière
  • SetAssocCache(size_bytes, line_bytes, ways).access(address)Cette lecture est-elle un succès dans un cache LRU à plusieurs voies ?Des ensembles de lignes en ordre LRU
  • conflict_trace(n_lines, line_bytes, n_sets, rounds)Quelles adresses entrent toutes en collision dans un ensemble ?De l'arithmétique simple
  • column_trace(n_rows, row_bytes, passes)Quelles adresses la lecture d'une colonne touche-t-elle ?De l'arithmétique simple
  • miss_rates_by_ways(trace, size_bytes, line_bytes, ways_list)Comment davantage de voies changent-elles le taux de défauts ?SetAssocCache
  • study(bars)Que coûtent des lignes espacées de 4096 octets ?column_tracemiss_rates_by_ways

À retenir

  • Les voies suppriment les conflits. Un ensemble à plusieurs voies laisse cohabiter des lignes en collision ; LRU décide qui part quand l'ensemble est plein.
  • L'adresse choisit l'ensemble. Étiquette, indice d'ensemble et déplacement viennent directement de l'adresse : la disposition de vos données décide donc quelles lignes entrent en collision.
  • Évitez les lignes puissance de deux. Les tailles de ligne multiples de l'étendue d'un ensemble mettent toute une colonne dans un ensemble ; une ligne de remplissage règle le problème.
Vérification rapide
Un cache de 32 Ko à lignes de 64 octets et 8 voies. Combien d'ensembles a-t-il ?
Choisissez une réponse.

Sujet suivant : le cache garde maintenant les bonnes lignes quand elles n'entrent pas en collision, mais combien de temps les lignes restent-elles utiles ? Dans Localité temporelle et ensembles de travail, vous mesurez à quelle vitesse une valeur est relue et trouvez la taille de cache à partir de laquelle un programme tient.

Lab : ce qui est noté

6 tâches · 100 pts
  • Étiquette, ensemble et déplacement10 pts
  • Un cache associatif LRU30 pts
  • Des lignes qui entrent en collision10 pts
  • Lire une colonne10 pts
  • Le taux de défauts selon le nombre de voies15 pts
  • L'étude sur la table SPY25 pts
Explorateur · lab-cl-3
▾ lab-cl-3
▾ data
…
bash · dans le navigateur
learner@quantcraft:~/lab-cl-3$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-cl-3 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes