...

Linux Transparent Page Cache: basisprincipes en verschillen met de klassieke page cache

In twee zinnen leg ik uit hoe Linux de toegang tot bestanden in het RAM versnelt en hoe een transparante paginacache gebruikmaakt van grotere paginablokken om de administratieve rompslomp te verminderen. Daarnaast leg ik de verschillen uit met de klassieke paginacache met 4-KiB-pagina’s, evenals het effect op de TLB, fragmentatie en het gedrag van de werklast.

Centrale punten

  • Paginagrootte: 4 KiB versus 2 MiB beïnvloedt de granulariteit en efficiëntie.
  • TLB-afdruk: Grote sites schrappen vermeldingen, kleine blijven flexibel.
  • Versnippering: Grote websites hebben aaneengesloten RAM nodig.
  • Werklasten: Bij sequentieel gebruik is de winst groot, bij willekeurig gebruik iets minder.
  • Controle: Testen, meten en vervolgens stap voor stap configureren.

Wat is de klassieke Linux-paginacache?

De klassieke paginacache bewaart veelgebruikte bestands pagina’s in het werkgeheugen, zodat leesbewerkingen direct vanuit RAM plaatsvinden. Het werkt doorgaans met pagina’s van 4 KiB en beheert elke pagina als een afzonderlijke eenheid in de cache. Hierdoor blijven veel kleine bestanden of veelgevraagde delen van grote bestanden beschikbaar, zonder de SSD of HDD te belasten. De kernel geeft voorrang aan actieve pagina’s, verwijdert inactieve inhoud en reageert zo dynamisch op pieken in de belasting. Voor meer achtergrondinformatie verwijs ik naar een beknopte inleiding over de Prestaties van de paginacache, waarin het basisprincipe op een praktijkgerichte manier wordt beschreven.

Waarom een transparante paginacache?

Veel afzonderlijke 4-KiB-pagina’s zorgen voor extra administratief werk en verhogen de druk op de TLB. Grotere pagina's, zoals 2 MiB, kunnen dezelfde adresruimte met minder vermeldingen bestrijken en zo CPU-tijd besparen. Een transparante paginacache voegt bestands pagina's automatisch samen tot grotere eenheden, wanneer toegangs patronen en de opslaglocatie dit toelaten. Dit lijkt op het idee achter Transparent Huge Pages, maar heeft hier betrekking op een op bestanden gebaseerde cache in plaats van anoniem geheugen. Ik pas dergelijke functies pas toe nadat ik de toegangspatronen, fragmentatie en latentie-eisen heb begrepen, omdat grotere pagina’s de granulariteit verhogen.

Verschillen systematisch vergelijken

Om een duidelijk overzicht te geven, zet ik de belangrijkste kenmerken van de klassieke cache, de transparante paginacache en THP naast elkaar, zodat de keuze per Werkbelasting gaat gemakkelijker. De nadruk ligt op paginagrootte, TLB, fragmentatie, voordelen en risico’s. De tabel toont sterke punten en beperkingen zonder marketingpraatjes. Ik lees de tabel van links naar rechts en kijk welke kolom het beste bij de belasting past. Vervolgens beslis ik of ik bij de 4-KiB-cache blijf of grotere pagina's ga testen.

Functie Klassieke paginacache (4 KiB) Transparante paginacache (bijv. 2 MiB) THP (anonieme opslag)
Paginagrootte/granulariteit Fijne, uiterst nauwkeurige caching Grofweg, hele grote gebieden Grof, grote hopen/stapels
TLB-afdruk Hoger door veel vermeldingen Lager, minder vermeldingen Lager, minder vermeldingen
Administratieve kosten Hoog bij veel pagina’s Minder metagegevens Minder metagegevens
Versnippering Niet-kritisch, vereist geen contiguïteit Vereist aaneengesloten RAM Vereist aaneengesloten RAM
Geschikte lasten Kleine bestanden, willekeurige toegangen Grote bestanden, sequentiële patronen Grote heaps, databases in het RAM-geheugen
Risico's Meer TLB- en CPU-overhead Overfetch, pieken in de latentie bij split/merge Overfetch, pieken in de latentie bij split/merge
Afhankelijkheid van kernel/feature Op grote schaal beschikbaar Let op de versie/implementatie Distributie-instellingen controleren

De tabel is geen vervanging voor een test, maar geeft structuur aan mijn Besluit. Ik beoordeel eerst de toegangs patronen en de bestandsgrootte. Vervolgens meet ik de latentie, de CPU-tijd en de cache-hitrate, zowel met als zonder grote pagina’s. Als benchmarks duidelijke voordelen laten zien zonder uitschieters, schaal ik voorzichtig op. Als er pieken optreden, rol ik terug of beperk ik het gebruik.

Hoe de kernel grote bestandspagina’s vormt

Om grotere paginablokken in de paginacache te creëren, heeft de kernel aaneengesloten bestandsgebieden in het geheugen nodig en voldoende coherente toegang. Een typisch voorbeeld is een ‘promotion’: meerdere pagina’s van 4 KiB worden samengevoegd tot een groter folio. Omgekeerd vindt bij ongeschikte patronen een splitsing plaats, waarbij de eenheden weer worden opgesplitst in kleinere eenheden. Ik observeer deze overgangen vooral onder belasting, omdat promotie en splitsing kortstondig CPU-capaciteit kosten en LRU-lijsten actualiseren. Sequentiele lezers bevorderen promotie, terwijl sterk verspreide workloads eerder splitsingen veroorzaken.

Readahead speelt hierbij een essentiële rol: wanneer er voldoende gegevens van tevoren worden ingelezen en deze gegevens vervolgens daadwerkelijk worden gebruikt, ontstaan er als het ware terloops grote folio’s. Als applicaties daarentegen in kleine, onvoorspelbare stappen gegevens ophalen, blijft de cache gedetailleerd. Ook Writeback werkt samen met grote pagina’s: als er veel aaneengesloten ‘dirty pages’ tegelijkertijd worden teruggeschreven, kan dit de doorvoersnelheid en het aantal IOPS ten goede komen, maar nemen de burst-groottes wel toe. Ik houd daarom rekening met de ‘dirty tuning’-parameters (bijv. vm.vuile_achtergrond_bytes en vm.dirty_bytes), om te grote flush-golven te voorkomen.

Bestandssystemen, I/O-paden en hun invloed

Buffered I/O profiteert rechtstreeks van de paginacache, Direct I/O (O_DIRECT) heeft daar grotendeels geen invloed op. Voor databases of back-uptools die bewust gebruikmaken van Direct I/O, heeft een transparante paginacache dus minder invloed. Bij mmap() hangt het effect af van het toegangs patroon: pagina voor pagina, voorwaarts lopende scans maken goed gebruik van grotere folio’s; willekeurige sprongen niet. Met posix_fadvise() kan ik de kernel aanwijzingen geven (bijv. SEQUENTIAL, WILLNEED, RANDOM), die de readahead en de verdringing sturen. Dergelijke hints zijn geen garanties, maar ze vergroten de kans dat de cache bij mijn workload past.

Bestandssystemen hebben hun eigen heuristieken. Op sommige systemen reageren ext4 en XFS heel redelijk op sequentiële stromen, terwijl Copy-on-Write-bestandssystemen met deduplicatie of compressie (bijvoorbeeld bomen met veel snapshots) andere uitvoerprofielen vertonen. Ik controleer daarom of de indeling en fragmentatie van het bestandssysteem grote aaneengesloten gebieden toelaten. Een defragmentatiebeurt voor sterk gefragmenteerde gegevens kan meetbare voordelen opleveren, maar moet altijd met de nodige voorzichtigheid en binnen onderhoudsvensters worden gepland.

Hardwarefactoren: architectuur, NUMA en apparaten

Niet elke architectuur gebruikt 4 KiB als basispagina. Op systemen met grotere basispagina’s veranderen de granulariteit en het TLB-gedrag al standaard. Dit verschuift het nut van grote folio’s in de cache. Daarnaast houd ik rekening met NUMA-topologieën: Grote pagina’s werken het beste als ze zich lokaal bevinden ten opzichte van de CPU die de I/O-thread of de applicatie uitvoert. Daarom koppel ik workers aan knooppunten, houd ik per-NUMA-statistieken in de gaten en voorkom ik onnodige toegang op afstand. Onder Linux helpen mij per-knooppunt-metrieken (/sys/devices/system/node/node*/meminfo) en het vastzetten van de scheduler om de lokaliteit te behouden.

Aan de apparaatzijde kijk ik naar de wachtrijen van de controller, de NVMe-diepte en de latentiecurve. Grote sites presteren goed met een hoge doorvoer en stabiele latentie, maar zijn gevoelig voor pieken in de tail-latentie. Een I/O-scheduler die burst-belastingen afvlakt, kan hier het verschil maken. Readahead-waarden (blockdev --getra/--setra) kalibreer ik zorgvuldig per apparaat en per workload.

Meetmethodiek, KPI's en observeerbaarheid

Ik definieer vooraf een klein aantal, maar veelzeggende indicatoren: page-fault-ratio, cache-hitratio, CPU-tijd per verzoek, TLB-belasting, readahead-hits, latentiepercentielen (P50/P95/P99) en I/O-mislukte toegangen. Voor het systeemoverzicht gebruik ik vmstat, sar -B, iostat en pidstat, om trends te herkennen. /proc/meminfo en smaps helpen bij het achterhalen wat er actief in het werkgeheugen staat; plaattop toont metadata-overhead. Indien nodig meet ik met perf TLB-misses en CPU-cycli onder reële belasting, om het effect van grote pagina’s zichtbaar te maken.

Een testrun bestaat voor mij uit drie fasen: opwarmen tot een stabiele hitrate, meetinterval onder gecontroleerde belasting, en afkoelen om eviction en writeback te observeren. Ik herhaal runs met dezelfde dataset en wisselende parameters (bijv. readahead, THP-modus altijd/slecht advies/nooit), om betrouwbare conclusies te kunnen trekken. Uitschieters laat ik niet buiten beschouwing: als P99 slechter wordt, terwijl het gemiddelde daalt, past de opzet meestal niet binnen mijn streefbandbreedte.

Typische voorbeelden uit de praktijk

Streaming- en media-workloads lezen grote bestanden grotendeels voorwaarts. Hier scoren grote folio’s regelmatig goed, omdat de druk op de TLB en de administratieve lasten afnemen. Back-up/herstel en replicatie met lange, sequentiële blokken bieden vergelijkbare voordelen, vooral wanneer meerdere processen dezelfde gebieden lezen. Machine learning-pijplijnen profiteren ervan wanneer datasets worden gebundeld en in de cache worden bewaard; sterk willekeurige steekproeven uit veel kleine bestanden dempen dit effect echter, tenzij men vooraf overschakelt op containerformaten met aaneengesloten blokken.

Build- en CI-omgevingen met duizenden kleine bestanden werken meestal beter met een granulariteit van 4 KiB. Daar is snelle, nauwkeurige beschikbaarheid van veelgebruikte fragmenten van belang. Ik investeer hier in een hoog RAM-aandeel voor Active(file), zinvolle readahead per apparaat en eventueel in applicatiespecifieke caches (bijv. dependency-caches), in plaats van grote pagina’s in de kernel af te dwingen.

Bronnenbeheer: Cgroups en working set-bescherming

In multi-tenant-omgevingen beperk en bescherm ik het geheugen per service. Met cgroup v2 kunnen processen die veel paginacache gebruiken nauwkeurig worden afgerekend en indien nodig via geheugen.laag beschermen, zodat belangrijke werkgeheugensets minder vaak worden verdrongen. geheugen.hoog stelt zachte bovengrenzen vast, geheugen.max strikte limieten. Ik observeer hoe ‘fairness’ en ‘eviction’ werken wanneer meerdere services dezelfde hostcache delen. Grote pagina’s kunnen hier helpen om de CPU te ontlasten, maar kunnen ook leiden tot grotere verdringingsblokken. Daarom stel ik de beschermingslimieten in kleine stapjes af en controleer ik de LRU-dynamiek.

Foutpatronen en tegenmaatregelen

Als promotie en split vaak samen voorkomen, zie ik schommelingen in de latentie, een hoog kernel-CPU-gebruik en een wisselende hitrate. Oplossing: readahead aanpassen, split-cascades vermijden, workloads ontkoppelen of de agressiviteit van grote pagina’s verminderen. Bij overfetch-symptomen (veel in de cache, toenemende swapdruk, dalende hitrate voor kleine hotsets) schakel ik over naar een fijnere granulariteit of isoleer ik grote lezers op speciale knooppunten. Als writeback-bursts de tail-latentie verhogen, stel ik strengere limieten voor dirty bytes in en maak ik de flush-intervallen gelijkmatiger.

NUMA-jitter los ik op door middel van CPU/geheugen-pinning en een zorgvuldige plaatsing van de I/O-threads. Als er TLB-misses optreden, maar de applicatie onverminderd traag blijft, controleer ik op lock-contention, bestandssysteemvergrendelingen en het effect van compressie/decryptie in de stack. Een prestatiewinst door grote pagina's is pas echt een succes als deze zich aan het eindpunt van de applicatie manifesteert.

Praktisch tijdschema voor toetsen

Ik begin met een baseline: de huidige kernel, THP-status (/sys/kernel/mm/transparent_hugepage/), readahead-waarden, I/O-scheduler, bestands- en schijfinrichting. Vervolgens stel ik twee tot drie concrete hypothesen op (bijv. „sequentiële mediastreams: -10% CPU, stabielere P99“). Vervolgens stel ik vaste datasets en belastingprofielen vast die realistische verkeerspatronen weergeven. Elke testreeks krijgt identieke opwarmtijden, identieke duur en identieke metrische registratie.

Ik varieer telkens slechts één parameter: eerst readahead, dan de agressiviteit bij grote pagina’s, en ten slotte de LRU/Dirty-instellingen. Na elke stap sla ik de statistieken en aantekeningen op, zodat latere kernel-updates vergelijkbaar blijven. Pas wanneer twee onafhankelijke runs dezelfde trend laten zien en de P95/P99-latenties stabiel zijn, breng ik de wijziging door naar een beperkte productiegroep. Een rollback-plan met duidelijke drempelwaarden (bijv. „P99 > +15% gedurende 5 min“) hoort daar altijd bij.

Toegangspatronen en gevoeligheid

Sequentiële lezers die met grote bestanden werken, hebben vaker baat bij grotere Pagina's. Willekeurige toegangen tot veel kleine bestanden verlopen meestal beter met 4 KiB, omdat de cache dan alleen de benodigde fragmenten in voorraad houdt. Gemengde werkbelastingen vereisen metingen met realistische datasets, aangezien synthetische tests vaak te optimistisch uitvallen. Ik let erop of overfetch geheugen in beslag neemt dat elders ontbreekt. Een kleine winst in CPU-tijd is niet de moeite waard als daardoor de LRU-druk toeneemt en de latenties omhoogschieten.

Webhostingscenario's met veel kleine bestanden

Bij typische shared hosting worden talloze kleine scripts, afbeeldingen en bestanden verwerkt, die de cache van 4 KiB prima aankan Handgreep heeft. Grote paginas bieden hier zelden een meerwaarde, aangezien bestanden vaak kleiner zijn dan 2 MiB of onregelmatig worden gebruikt. In plaats daarvan investeer ik in voldoende RAM, zinvolle readahead per apparaat en caches op applicatieniveau, zoals OPCache. Daarnaast controleer ik of statische assets via een HTTP-cache sneller worden geleverd dan vanaf het blokapparaat. Pas wanneer belastingsprofielen grotere bestanden laten zien, sta ik grotere paginacache-pagina’s toe.

Databases, caches en logbestanden

In-memory-databases en grote heaps profiteren vaak van THP in de anonieme modus Geheugen. Bij bestandsgebaseerde engines en log-pijplijnen met lange, sequentiële leesbewerkingen kan een transparante paginacache eveneens voordelen opleveren. Ik test op reproduceerbare wijze of het aantal paginastoringen afneemt en de CPU rustiger draait. Tegelijkertijd kijk ik of overfetch het gebruikte RAM-geheugen doet stijgen en of de opstarttijden veranderen. Een korte toelichting helpt bij de start: ik gebruik deze handleiding om THP beoordelen en de wisselwerkingen correct in te schatten.

Virtualisatie en containers

Meerdere VM's of containers delen de host-kernel en daarmee de Pagina-Cache. Veelgebruikte binaire bestanden en bibliotheken worden dan vanuit dezelfde cache aan alle instanties geleverd, wat I/O bespaart. THP in de guest kan de CPU-belasting verminderen, maar vereist wel dat er rekening wordt gehouden met NUMA-zones en overcommit. Ik meet per NUMA-node, zodat grote pagina's niet door het hele systeem hoeven te reizen. Als er onder belasting jitter optreedt, verlaag ik de agressiviteit (madvise) of schakel ik THP selectief uit, totdat de curven weer vloeiend zijn.

Configuratie controleren en op de juiste manier instellen

Ik begin met een nuchtere Inventaris: Welke kernelversie, welke standaardinstellingen, welke mount-opties, welke readahead-waarden? De status van THP controleer ik onder /sys/kernel/mm/transparent_hugepage/ (bijv. enabled, defrag, khugepaged). Voor het gedrag van de paginacache kijk ik naar /proc/meminfo, per-node-statistieken en readahead per blok. Ik voer wijzigingen nooit blindelings door, maar test ze eerst op een staging-omgeving met echte gegevens. Pas daarna neem ik stabiele configuraties over naar de productieomgeving.

Fijnafstemming: readahead, eviction en monitoring

Grote pagina’s werken alleen als readahead, de I/O-scheduler en LRU goed functioneren samenspelen. Ik houd de page-fault-rate, misses, CPU-tijd en eventuele latentiepieken bij het splitsen en samenvoegen van grote pagina’s in de gaten. Onder druk ben ik benieuwd hoe snel de cache oude pagina’s verdringt en of er belangrijke bestanden uitvallen. Een goed startpunt om naar verdringing te kijken, is dit artikel over Uitzetting onder druk van het geheugen, waarin de typische patronen worden toegelicht. Daarna pas ik ‘readahead’, de opties voor het bestandssysteem en, indien nodig, het gebruik van grote pagina’s voorzichtig aan.

Checklist voor de praktijk zonder mythes

Ik begin met duidelijke doelstellingen: minder CPU-tijd, een stabielere latentie, passende Raakpercentage in de paginacache. Vervolgens definieer ik meetpunten en kies ik reële workloads die pieken en gemengde belastingen vertonen. Daarna test ik stapsgewijs grotere pagina’s, eerst op de staging-omgeving en daarna op beperkte schaal in productie. Ik zorg dat ik rollback-plannen achter de hand heb voor het geval er overfetch, fragmentatie of jitter optreedt. Tot slot documenteer ik de effecten, zodat de opstelling reproduceerbaar blijft en latere kernel-updates kunnen worden geëvalueerd.

Kort samengevat

De klassieke cache van 4 KiB blijft voor veel toepassingen de betrouwbare Basis, omdat het gedetailleerd en zuinig omgaat met RAM. Een transparante paginacache vermindert de druk op de TLB en de hoeveelheid metadata wanneer grote bestanden sequentieel worden gelezen. THP richt zich op anonieme geheugengebieden en kan grote heaps ondersteunen, maar vereist zorgvuldigheid vanwege mogelijke latentiepieken. Ik neem de beslissing op basis van gegevens: meten, vergelijken en vervolgens implementeren. Wie zo te werk gaat, bereikt voorspelbare responstijden, een zinvol RAM-gebruik en een merkbaar rustigere CPU.

Huidige artikelen

Datacenter met serverracks en gestileerde datavisualisatie voor het optimaliseren van de MySQL-prestaties
Databases

MySQL-histogrammen – Betere queryplannen zonder index

Ontdek hoe MySQL-histogrammen de optimizer voorzien van nauwkeurige optimizer-statistieken, betere queryplannen mogelijk maken en je SQL-tuning aanzienlijk verbeteren zonder dat je extra indexen hoeft toe te voegen.