Ke zlepšení výkonu přístupu k datům se běžně používá mezipaměť (cache). Pokud existuje více mezipamětí pro stejný paměťový prostředek (např. v každém jádru procesoru vlastní cache), může to vést k problémům. Koherence mezipaměti označuje soubor principů a mechanismů, které zajišťují, aby všechny mezipaměti obsahující kopii téhož paměťového bloku měly konzistentní a smysluplná data (tj. zachování integrity dat). Koherence mezipaměti je specifickým případem obecnějšího problému koherence paměti.

Problém nastává, když více mezipamětí drží kopie stejného paměťového bloku a alespoň jedna z nich tento blok změní. Například v systému, jaký popisuje následující obrázek, může horní klient mít v mezipaměti starou kopii bloku z předchozího čtení a dolní klient tento blok přepíše. Horní klient by pak mohl pokračovat v práci se zastaralými daty, aniž by o tom věděl. Koherence mezipaměti se stará o detekci a zvládání takových konfliktů, aby mezipaměti i hlavní paměť zůstaly v souladu.

Proč je koherence důležitá

  • Zabraňuje tomu, aby různé procesory pracovaly s odlišnými hodnotami téže proměnné.
  • Zajišťuje předvídatelné chování programů pracujících nad sdílenou pamětí.
  • Snižuje počet chyb způsobených závodními podmínkami a zastaralými daty.

Typické problémy spojené s nekoherencí

  • Stará (neplatná) data: cache obsahuje hodnotu, která byla změněna jinde.
  • Konflikty zápisu: dva klienti zapisují do stejného bloku bez koordinace.
  • False sharing: dva procesy zapisují do různých proměnných, které leží na stejné cache lince — opakované invalidace zhoršují výkon, i když data nejsou logicky sdílena.

Základní principy protokolů koherence

Pro udržení koherence se používají protokoly, které definují, jak se mají mezipaměti vzájemně informovat o čteních a zápisech. Mezi základní principy patří:

  • Sledování vlastnictví: která cache vlastní aktuální (nejnovější) verzi bloku.
  • Invalidace vs. aktualizace: při zápisu se ostatní kopie buď invalidují (write-invalidate), nebo se jim pošle aktualizovaná hodnota (write-update).
  • Koordinace přes sběrnici nebo adresář: mechanismus, kterým se šíří informace o změnách (snooping na shared bus, nebo adresářové/ directory-based řešení v rozsáhlejších systémech).

Hlavní typy protokolů

  • Snooping (sledující) protokoly: každá cache „poslouchá“ (snoops) na sdílené sběrnici a reaguje na transakce (čtení/zápis). Vhodné pro menší vícejádrové systémy se společnou sběrnicí.
  • Adresářové (directory-based) protokoly: centrální nebo distribuovaný adresář sleduje, které cachi drží kopii konkrétního bloku; škáluje lépe u velkého počtu jader a v distribuovaných systémech.
  • Write-invalidate vs. write-update: write-invalidate při zápisu pošle zprávu ostatním cache, aby invalidovaly své kopie; write-update rozesílá novou hodnotu do všech kopií. Write-invalidate bývá častější díky nižší režii přenosu při opakovaných zápisech vlastníka.

Stavy v protokolech (příklad MESI)

Jedním z běžných protokolů je MESI, který rozlišuje čtyři stavy cache linie:

  • Modified (M) – linie je modifikovaná a v mezipaměti; hlavní paměť není aktuální.
  • Exclusive (E) – linie je exkluzivně v této cache a shoduje se s hlavní pamětí.
  • Shared (S) – linie může být ve více cache a shoduje se s hlavní pamětí.
  • Invalid (I) – linie není platná v této cache.

Existují i další varianty (MSI, MOESI), které přidávají další stavy (např. Owned) pro lepší výkon v určitých scénářích.

Řešení konfliktů a techniky

  • Hardwareové protokoly: implementované v řadičích mezipaměti; zahrnují snooping, adresářové tabulky a stavové stroje (MESI, MOESI).
  • Software a synchronizace: programátoři používají zámky, semafory, atomické operace a paměťové bariéry (memory fences) k zajištění správné viditelnosti změn mezi vlákny.
  • Minimalizace sdílení: návrh datových struktur tak, aby vláken bylo co nejméně nuceno sdílet stejné cache linie (padding, oddělení dat pro každé vlákno).
  • Granularita koherence: snížení falešného sdílení změnou velikosti nebo zarovnání objektů tak, aby související data neležela na stejné cache lince.
  • Adresářové řešení pro škálovatelnost: v rozsáhlých vícejádrových nebo distribuovaných systémech je adresářová koherence efektivnější než broadcast přes sběrnici.

Dopady na návrh softwaru a hardwaru

Koherence mezipaměti ovlivňuje:

  • Výkon: nesprávné chování (časté invalidace, falešné sdílení) může výrazně zhoršit výkon.
  • Škálovatelnost: některé protokoly neškálují dobře s rostoucím počtem jader; adresářové přístupy a hierarchická řešení pomáhají škálovat.
  • Programovací model: slabší paměťové modely a optimalizace kompilátoru vyžadují explicitní synchronizaci, aby programy byly korektní.

Praktická doporučení

  • Minimalizujte sdílení mutable dat mezi vlákny — pokud je to možné, používejte lokální kopie nebo immutable struktury.
  • Vyhněte se false sharingu: zarovnejte nebo oddělte často zapisovaná pole, případně použijte padding tak, aby každé vlákno pracovalo s jinou cache linkou.
  • Používejte atomické operace a paměťové bariéry tam, kde záleží na pořadí viditelnosti zápisů.
  • Testujte výkonnost vícevláknových částí aplikace a profilujte cache-missy; často drobné změny v rozvržení dat přinesou velký benefit.

Koherence mezipaměti je tedy kombinací hardwarových protokolů a softwarových postupů, které společně zajišťují, že více kopií téhož datového bloku zůstane konzistentních. V praxi jde vždy o kompromis mezi přesností (silnou konzistencí), latencí, propustností a škálovatelností řešení.

V kontextu moderních vícejádrových systémů je běžným případem použití koherence mezipaměti právě koherence procesorů ve víceprocesorovém systému — tam, kde je potřeba zajistit, aby všechny jádra viděla konzistentní verzi sdílených dat.