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.

