Qu'est-ce que Cohérence séquentielle ?

Modèle de cohérence mémoire le plus intuitif : les opérations de tous les cœurs s'ordonnent en une séquence unique, où celles de chaque cœur apparaissent dans l'ordre du programme.

Nom japonais: 逐次一貫性

« Cohérence séquentielle » apparaît dans 3 questions d'entraînement réparties sur 1 thèmes, dont Multicœur et synchronisation.

Domaine: Multicœur et synchronisation

Ce que garantit le modèle

La cohérence séquentielle, définie par Leslie Lamport en 1979, exige que le résultat de toute exécution soit le même que si les opérations mémoire de tous les cœurs étaient entrelacées en une seule séquence globale, les opérations de chaque cœur y apparaissant dans l'ordre du programme. Comme lorsqu'on mélange plusieurs paquets de cartes sans réordonner aucun paquet pris isolément, l'entrelacement peut être quelconque, mais tous les cœurs voient le même. Les programmeurs peuvent raisonner sur du code concurrent en imaginant que les threads s'exécutent à tour de rôle, ce qui explique qu'il soit considéré comme le modèle de cohérence mémoire le plus intuitif.

Pourquoi les vrais processeurs l'assouplissent

Préserver strictement l'ordre du programme pour tous les accès mémoire interdit de nombreuses optimisations matérielles. Un tampon d'écriture permet à un cœur de continuer à s'exécuter après une écriture sans attendre qu'elle atteigne le cache, et l'exécution dans le désordre permet à des chargements ultérieurs de s'achever avant des écritures antérieures. Ces deux mécanismes rompent la cohérence séquentielle. La plupart des processeurs implémentent donc des modèles plus faibles : x86 utilise l'ordre total des écritures (TSO), qui permet à un chargement ultérieur de doubler une écriture antérieure vers une adresse différente, et Arm et RISC-V utilisent des modèles encore plus faibles qui autorisent davantage de réordonnancements, sauf si le programme insère des fences.

Le test décisif du tampon d'écriture

Supposons que le cœur 0 exécute x = 1 puis r1 = y, tandis que le cœur 1 exécute y = 1 puis r2 = x, avec x et y initialement à 0. Sous cohérence séquentielle, l'une des écritures doit venir en premier dans l'ordre global ; au moins une lecture voit donc 1, et r1 = r2 = 0 est impossible. Sur x86, chaque écriture peut rester dans le tampon d'écriture de son cœur pendant que la lecture suivante récupère l'ancienne valeur dans le cache ; les deux lectures peuvent donc renvoyer 0. Placer une barrière mémoire complète entre l'écriture et la lecture sur chaque cœur rétablit le résultat séquentiellement cohérent.

Cohérence séquentielle et cohérence de cache

La cohérence de cache concerne un seul emplacement mémoire : tous les cœurs s'accordent sur l'ordre des écritures à cette adresse et finissent par voir la dernière valeur. Un modèle de cohérence mémoire comme la cohérence séquentielle concerne l'ordre des accès à des adresses différentes, que la cohérence de cache seule ne contraint pas. Les langages comblent l'écart : les atomiques de C11 et C++11 utilisent par défaut memory_order_seq_cst, et les programmes exempts de courses aux données se comportent comme s'ils étaient séquentiellement cohérents. Des ordres plus faibles, comme acquire/release ou relaxed, permettent au compilateur d'émettre moins de barrières lorsque l'ordre complet n'est pas nécessaire.

S'entraîner : questions sur Cohérence séquentielle

Quelle description de la cohérence séquentielle (sequential consistency) est correcte ?

  1. ALes opérations de chaque cœur s’exécutent à chaque fois dans un ordre aléatoire
  2. BLes opérations de tous les cœurs paraissent disposées dans un ordre unique, celles de chaque cœur y apparaissant dans l’ordre du programme
  3. CToutes les opérations s’exécutent sur un seul cœur
Afficher la réponse et l'explication

Réponse : B. Les opérations de tous les cœurs paraissent disposées dans un ordre unique, celles de chaque cœur y apparaissant dans l’ordre du programme

En cohérence séquentielle, les opérations des différents cœurs paraissent disposées en une seule file, comme des cartes mélangées, chaque cœur conservant l’ordre de son programme. C’est le modèle le plus intuitif pour le programmeur, mais il limite fortement les optimisations matérielles de réordonnancement, comme les tampons d’écriture ; la plupart des CPU réels adoptent donc un modèle plus relâché.

Questions fréquentes

x86 est-il séquentiellement cohérent ?

Non. x86 implémente l'ordre total des écritures (TSO), qui permet à un chargement de s'achever avant qu'une écriture antérieure vers une adresse différente ne devienne visible aux autres cœurs ; le résultat r1 = r2 = 0 du test du tampon d'écriture peut donc se produire. Une barrière comme MFENCE ou une instruction verrouillée (lock) l'empêche.

Quelle est la différence entre cohérence séquentielle et cohérence de cache ?

La cohérence de cache garantit un ordre unique et partagé des écritures pour chaque adresse prise individuellement. La cohérence séquentielle est plus forte et couvre l'ordre relatif des opérations sur toutes les adresses, sous la forme d'un entrelacement global unique qui respecte l'ordre du programme de chaque cœur.

Que signifie memory_order_seq_cst en C++ ?

C'est l'ordre par défaut des opérations std::atomic : toutes les opérations seq_cst apparaissent dans un ordre total unique, cohérent avec l'ordre du programme de chaque thread. C'est le plus simple pour raisonner, mais il peut exiger les instructions de barrière les plus coûteuses.

« Cohérence séquentielle » dans les explications

Questions mentionnant « Cohérence séquentielle » (3)

  1. Quelle description de la cohérence séquentielle (sequential consistency) est correcte ?
  2. Les deux threads ci-dessus s’exécutent simultanément sur des cœurs différents (sans réordonnancement par le compilateur). Quel résultat est impossible en cohérence séquentielle mais possible sur un CPU réel comme x86 ?
  3. Pourquoi _Atomic de C11 et std::atomic de C++11 permettent-ils de spécifier un ordre mémoire ?

Termes associés

Barrière mémoire Acquisition-libération (acquire-release) Compare-and-swap Course aux données (data race) Espionnage de bus (snooping) Faux partage (false sharing) Instruction atomique Interblocage (deadlock) LR/SC Mémoire transactionnelle

Retour au glossaire