En cache kan användas för att förbättra prestandan vid åtkomst till en viss resurs. När det finns flera sådana cacher för samma resurs kan det leda till problem om de inte hålls i synk. Cachekoherens eller Cache coherency avser de mekanismer som ser till att alla cacheminnen för en resurs innehåller konsekventa och meningsfulla data (så kallad dataintegritet). Cachekoherens är ett specialfall av minneskoherens och är särskilt viktigt i system där flera klienter eller processorer kan läsa och skriva samma minnesområden samtidigt.
Problem uppstår när kopior av samma minnesblock finns i flera cacheminnen. Om en klient skriver till sin cache utan att övriga cachar uppdateras eller informeras, kan andra klienter få gamla eller felaktiga värden. Ett vanligt exempel är CPU:ernas cache i ett flerprocessorsystem: om en processor har en kopia av ett minnesblock från en tidigare läsning och en annan processor ändrar det minnesblocket, kan den första processorn stå kvar med en ogiltig eller föråldrad cache utan att veta om det. Cachekoherensmekanismer syftar till att hantera sådana konflikter och säkerställa att alla cacher och huvudminnet håller sig i ett konsistent tillstånd.
Varför cachekoherens är viktig
- Korrekthet: Utan koherens kan program få olika, oförutsägbara resultat eftersom läsningar kan returnera gamla värden.
- Prestanda: Rätt designade koherensprotokoll minskar onödiga minnesåtkomstlatens genom att tillåta lokala cachar att användas säkert.
- Skalbarhet: I stora flerkärniga eller distribuerade system blir hantering av delat data avgörande för att undvika fel och flaskhalsar.
Vanliga orsaker till inkonsekvens
- Parallella skrivningar: Två eller flera klienter skriver till samma minnesadress utan synkronisering.
- Föråldrade kopior: En cache innehåller en tidigare version efter att en annan cache uppdaterat värdet.
- Fördröjda uppdateringar mellan cache och huvudminne.
Strategier för att upprätthålla koherens
Det finns två huvudstrategier för att hålla cachar koherenta:
- Write-invalidate: När någon skriver till ett block invalidiseras kopior i andra cacher. Skrivarens cache får ensam skrivbehörighet. Detta minskar antalet skrivmeddelanden men kan leda till fler cache-missar för andra läsare.
- Write-update (write-broadcast): När ett block skrivs sänds den nya datan till alla andra cacher så att de uppdateras. Detta bevarar läsarnas kopior men ökar nätverkstrafiken eller bussbelastningen.
Koherensprotokoll — exempel
Maskinvaruimplementerade protokoll används ofta i CPU-cacher. Vanliga protokoll:
- MSI: Tre tillstånd — Modified, Shared, Invalid. Enklare men mindre effektivt i vissa scenarier.
- MESI: Fyra tillstånd — Modified, Exclusive, Shared, Invalid. Tillåter att en cache innehar en exklusiv oförändrad kopia (Exclusive) vilket minskar busskommunikation vid läsning följt av skrivning.
- MOESI och andra varianter: Inkluderar även ett Owned-state för att optimera skrivning utan omedelbar skrivning tillbaka till huvudminnet.
Snooping vs directory-baserade protokoll
- Snooping: Alla cachekontrolllogiker "lyssnar" på en gemensam buss. När en cache skriver eller frågar om ett block kan andra cacher agera direkt. Lämpligt för mindre system med snabb gemensam buss.
- Directory-baserat: En central katalog (directory) håller reda på vilka cacher som har kopior av varje block. Skalbart till fler noder och används ofta i stora distribuerade minnessystem.
Cachekoherens vs minneskonsistens
Det är viktigt att skilja mellan begreppen:
- Cachekoherens: Handlar om att alla kopior av samma minnesplats i olika cacher överensstämmer — att uppdateringar når eller invalidiserar andra kopior.
- Minneskonsistens (memory consistency): Handlar om i vilken ordning en processors minnesoperationer blir synliga för andra processorer (exempelvis sekvensen av läsningar och skrivningar). Korrekt koherens är en förutsättning, men konsistensmodellen beskriver programmets semantik över tid.
Utmaningar och prestandatrade-offs
- Kommunikationskostnad: Särskilt i stora system kan koherensmeddelanden bli en flaskhals.
- False sharing: Två trådar skriver till olika data som råkar ligga i samma cachelinje — leder till onödiga invalidiseringar och prestandaförluster.
- Skalbarhet: Snooping fungerar bra i mindre system, men directory-baserade lösningar krävs för att skala.
Praktiska exempel och åtgärder
- På mjukvarunivå: Använd lås, atomiska operationer och minnessynkronisering för att undvika konkurrens om delade data.
- På hårdvarunivå: Välj rätt koherensprotokoll och organisera data för att minimera false sharing (t.ex. genom att padd:a strukturer så att ofta ändrade variabler inte hamnar i samma cachelinje).
- Verktyg: Profileringsverktyg och simuleringar kan identifiera koherensrelaterade flaskhalsar.
Sammanfattning
Cachekoherens är en grundläggande funktion i flerkärniga och distribuerade system för att säkerställa att flera kopior av samma data inte blir inkonsekventa. Genom koherensprotokoll, lämpliga skrivpolicyer och genomtänkt programdesign kan man både upprätthålla korrekthet och minimera prestandapåverkan. För stora system används ofta directory-baserade protokoll, medan snooping-lösningar är vanliga i mindre, bussbaserade arkitekturer.

