...

PHP-preloading: prestatieverbeteraar voor moderne PHP 8-projecten

PHP Preloading in PHP 8 laadt centrale klassen en functies bij het opstarten van PHP-FPM in het geheugen en verkort zo de weg naar de eigenlijke applicatielogica aanzienlijk. Ik laat zien hoe ik Voorladen hoe ik dit kan combineren met OPcache, waar dit een meetbare snelheidswinst oplevert en hoe ik het veilig kan integreren in builds en deployments.

Centrale punten

Voordat ik dieper op de materie inga, vat ik de belangrijkste aspecten samen en zet ik ze in de juiste context voor de praktijk. Ik leg kort de relatie uit tussen OPcache en preloading, en leg ik uit waarom dit effect vooral bij grote frameworks van toepassing is. Vervolgens ga ik in op concrete cijfers over latentie, doorvoer en autoloading, zodat de verwachtingen realistisch blijven. Ik leg ook uit wanneer ik preloading gebruik en wanneer ik het achterwege laat om moeite te besparen. Tot slot geef ik tips voor configuratie, scripts, tests en een nette Herstart in bedrijf.

  • Mechanica: voorgecompileerde klassen/functies blijven beschikbaar voor het hele proces.
  • Prestaties: min 5–15 % TTFB, plus 30–50 % RPS mogelijk.
  • Automatisch laden: 10–16 ms per verzoek besparen.
  • Selectie: alleen stabiele kernmodules en een framework-basis integreren.
  • Inzet: Voor wijzigingen is een herstart van FPM met een plan vereist.

OPcache versus preloading: een kort overzicht

OPcache compileert bestanden bij de eerste aanroep naar bytecode en slaat deze op in het geheugen, terwijl Voorladen gericht en eenmalig bij het opstarten plaatsvindt. Ik gebruik preloading om kernklassen vooraf te compileren en permanent in een persistent geheugengedeelte van OPcache te bewaren. Daardoor zijn essentiële symbolen direct beschikbaar, zonder autoloading, het parseren van bestanden of include/require. De normale OPcache kan vermeldingen weggooien bij geheugen- of tijddruk, maar de vooraf geladen elementen blijven behouden. Zo bespaar ik I/O, verhelp ik vertragingen bij een koude start en verminder ik de CPU-tijd in de vroege Bootstrap-fase van grote apps.

Hoe preloading de verzoekcyclus verkort

Bij een typische verzoek worden eerst honderden bestanden geladen voordat de controller en de businesscode worden uitgevoerd, en precies hier komt Voorladen. Ik sla kernonderdelen van frameworks zoals Symfony of Laravel vooraf op in de cache en voorkom zo dat veel bestanden herhaaldelijk moeten worden geparseerd. Dit verlaagt de Time To First Byte vaak met 5–15 % en biedt meer ruimte voor echte logica. Autoloading-ketens zijn niet meer nodig voor kernklassen, wat vooral merkbaar is bij responstijden onder de 200 ms. Onder belasting stijgt het aantal verzoeken per seconde, omdat er meer CPU-tijd wordt besteed aan de daadwerkelijke Toepassing voordelen.

Wanneer preloading echt werkt

Ik schakel preloading vooral in bij grote framework-stacks, API’s en webshopsystemen met veel klassen, omdat de koude start daar veel tijd kost. In dergelijke omgevingen leveren 30–50 % meer op RPS concreet voordeel, vooral als de hardware ongewijzigd moet blijven. Kleine scripts of eenvoudige pagina’s met weinig includes hebben hier nauwelijks baat bij, omdat de overhead gering is. WordPress komt goed van pas wanneer er op de achtergrond veel plug-ins en eigen bibliotheken aan het werk zijn. In alle gevallen is een zorgvuldige selectie van de te laden Bestanden, anders neemt de cache onnodig veel geheugen in beslag.

Beperkingen en valkuilen in het dagelijks leven

Het vooraf laden blijft ongewijzigd totdat ik de FPM-pool opnieuw start, en juist dat vereist discipline bij het Inzet. Zodra ik gewijzigde, vooraf geladen bestanden installeer, zien actieve processen nog steeds de oude bytecode. Daarom plan ik herstarts op een gecontroleerde manier en laad ik geen artefacten die vaak veranderen, zoals gegenereerde klassen, vooraf in. Ik let bovendien op het OPcache-geheugen en het maximale aantal versnelde bestanden, zodat er niets uit de cache verdwijnt. Wie zich dieper verdiept in inconsistente caches en herstarts, vindt achtergrondinformatie over de OPcache validatie, waarmee ik bij grote opstellingen altijd rekening houd.

OPcache- en Preload-configuratie in PHP 8

Om een goede start te maken, schakel ik OPcache in, stel ik de geheugencapaciteit in en definieer ik het preload-script inclusief de gebruiker, zodat er geen problemen met rechten ontstaan. Belangrijke instellingen zijn zend_extension, opcache.enable, memory_consumption, max_accelerated_files en de paden naar opcache.preload en opcache.preload_user. Ik kies daarbij voor consistente instellingen per FPM-pool, omdat een mengeling van instellingen al snel tot foutopsporing leidt. De volgende parameters gebruik ik als richtlijn en pas ik aan de projectomvang en Verkeer . Wie zich verder in de opties wil verdiepen, vindt praktische tips over de OPcache configuratie, die ik bij elke fijnafstelling controleer.

Instelling Voorbeeldwaarde Effect
opcache.enable 1 Geactiveerd OPcache wereldwijd.
opcache.geheugen_verbruik 256–512 Reserveert MB voor bytecode en symbolen.
opcache.max_versnelde_bestanden 20000–100000 Verhoogt het aantal opgeslagen bestanden.
opcache.preload /pad/naar/preload.php Definieert het Voorbelasting-script.
opcache.preload_user www-data Bepaalt de gebruiker die het programma uitvoert.

Een preload-script instellen

In preload.php som ik kernklassen expliciet op of compileer ik geselecteerde mappen recursief met opcache_compile_file(). Ik begin met de basis van het framework en stabiele modules uit src/, zodat ik een zo hoog mogelijk percentage treffers in het ‘hot path’ bereik. Het volledig laden van vendor-bestanden zorgt meestal voor een overbelasting van de cache en verhoogt Risico bij de implementatie. Het is beter om een korte whitelist voor de kern van het framework te gebruiken en eigen modules op een weloverwogen manier automatisch te integreren. Met opmerkingen en een versiecode in het script behoud ik het overzicht en regel ik herstarts bewust, in plaats van Toeval het veld af te staan.

Meten, valideren, bijstellen

Ik schakel preloading nooit zomaar in, maar meet eerst de basiswaarden voor TTFB, CPU-belasting, geheugen en RPS. Daarna pas ik de selectie van de bestanden aan en controleer ik opnieuw of het automatisch laden en het aantal bestandstoegangen afnemen. Eenvoudige verzoeklogboeken laten snel zien hoeveel includes er wegvallen en waar er nog Flessenhalzen op de loer liggen. Om de prestaties onder belasting te meten, gebruik ik herhaalbare benchmarks, bijvoorbeeld met identieke scenario’s per build. Als de cijfers kloppen, zet ik de preload-lijst vast en documenteer ik het proces in CI/CD.

Preloading integreren in DevOps- en implementatieprocessen

Ik integreer het preload-script in de build, laat de artefacten controleren en start aan het einde een geplande FPM-herstart. Bij rollbacks wordt altijd rekening gehouden met de vastgezette preload-versie, zodat oude processen consistent blijven. Blue/Green of Canary verminderen het risico, terwijl ik de nieuwe Configuratie uitrol. Tijdens onderhoudsvensters geef ik voorrang aan tijdelijke pools en stel ik schrijfintensieve bewerkingen uit totdat de knooppunten weer op temperatuur zijn. Zo houd ik pieken in de latentie binnen de perken en voorkom ik gemengde bytecode-toestanden op Servers.

Hostingstrategie: wanneer de configuratie van de server het verschil maakt

Een krachtige stack met PHP 8.x, snelle NVMe, voldoende RAM en de juiste OPcache-limieten zorgt ervoor dat preloading uitstekend presteert. Ik zorg ervoor dat FPM-pools op dezelfde manier zijn geconfigureerd en dat er voldoende bufferruimte overblijft voor de persistente bytecode. Afhankelijk van de projectfase pas ik het aantal processen, het geheugen en het aantal maximale bestanden aan om rommel in de cache te voorkomen. Bij versie-updates controleer ik op neveneffecten, omdat wijzigingen in de interne werking van de engine invloed hebben op bytecode kunnen hebben. Wie de installatie en de versies op een zinvolle manier op elkaar afstemt, profiteert daar meetbaar van; aanwijzingen over PHP-versie en hosting Ik gebruik dit als richtlijn bij het bepalen van de maat.

Praktische checklist voor projecten

Ik begin met een preload-pilot op Staging en verzamel betrouwbare voor-en-na-cijfers. Daarna selecteer ik de 50–200 meest belaste klassen uit het framework en de kernmodules, in plaats van de hele vendor te laden. Ik documenteer herstarts, koppel preload-versies aan builds en rol updates in groepen uit. Voor onderhoud bewaar ik scripts, OPcache-parameters en meetpunten in de repository, zodat elke wijziging traceerbaar blijft. Met deze aanpak bereik ik kortere TTFB, meer RPS en gelijkmatigere belastingscurves zonder verrassingen.

Fijnafstellingen die vaak over het hoofd worden gezien

Naast de kernparameters is het de moeite waard om eens te kijken naar een paar aanpassingsmogelijkheden die het resultaat stabiliseren:

  • opcache.interned_strings_buffer: Reken op 16–64 MB. Grote frameworks profiteren hiervan, omdat veel identieke strings (naamruimten, methodenamen) slechts één keer in het geheugen worden opgeslagen.
  • opcache.save_comments: Laat op 1 staan als er gebruik wordt gemaakt van attributen/annotaties. Wie opmerkingen weglaat, riskeert onverwacht gedrag bij Reflection en validatoren.
  • opcache.validate_timestamps: In de productieomgeving vaak 0, zodat OPcache niet voortdurend het bestandssysteem controleert. In combinatie met preloading klopt dit, omdat wijzigingen sowieso een herstart vereisen.
  • opcache.revalidate_freq: Als validate_timestamps=1 is (bijv. Staging), stel dan de frequentie hoger in (bijv. 60) om de belasting van het bestandssysteem te verminderen.
  • opcache.jit en jit_buffer_size: JIT zorgt zelden voor een grote prestatieverbetering bij web-workloads, maar neemt wel geheugen in beslag. Ik houd JIT op de achtergrond of schakel het uit, zolang het niet aantoonbaar nodig is, om te voorkomen dat het geheugen voor het vooraf laden wordt opgeslokt.

Geschikte kandidaten selecteren

De keuze is bepalend voor het effect en de stabiliteit. Ik ga daarbij op basis van gegevens te werk:

  • Include-statistieken: In het toegangslogboek of de profiler (Xdebug/Blackfire) kan ik zien welke bestanden per verzoek het vaakst worden geladen.
  • Composer-Classmap: Met de geoptimaliseerde autoloader (dump-autoload -o) heb ik een goede basis om stabiele naamruimten uit core en src te identificeren.
  • Kern van het framework: In Symfony bijvoorbeeld HttpKernel, EventDispatcher, Routing en de basis van de DI-container; in Laravel Foundation, Support en delen van Illuminate.
  • Eigen basismodules: Waardevolle objecten, utility-lagen, centrale interfaces en traits die in vrijwel elk verzoek worden gebruikt.

Niet vooraf laden: dynamisch gegenereerde artefacten (proxies, gecompileerde containers, caches), domeinklassen die tijdens actieve ontwikkeling sterk veranderen of zelden gebruikte beheer-modules.

Voorbeeld: robuust preload-script

Een korte, betrouwbare aanpak die alleen de gewenste delen compileert en dit netjes registreert:

<?php
// preload.php v1.3 - Auswahl nur stabiler Kernmodule

$root = __DIR__;
$paths = [
    $root . '/src/Domain',
    $root . '/src/Application',
    $root . '/vendor/symfony/http-kernel',
    $root . '/vendor/symfony/event-dispatcher',
    $root . '/vendor/illuminate/support',
];

// Hilfsfunktion: rekursiv PHP-Dateien kompilieren
function preload_dir(string $dir): void {
    $it = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($it as $file) {
        if ($file->isFile() && $file->getExtension() === 'php') {
            @opcache_compile_file($file->getPathname());
        }
    }
}

foreach ($paths as $path) {
    if (is_dir($path)) {
        preload_dir($path);
    }
}

// Einzeldateien, die nicht in obigen Ordnern liegen
$single = [
    $root . '/src/Kernel.php',
    $root . '/src/Infrastructure/Bootstrap.php',
];

foreach ($single as $file) {
    if (is_file($file)) {
        @opcache_compile_file($file);
    }
}

// Optional: Marker in die Error-Log ausgeben
error_log('[preload] completed at ' . date(DATE_ATOM));

Belangrijk: ik werk met absolute paden, vermijd side-effects van `require`/ingesloten bestanden in het preload-script en houd de lijst stabiel. `opcache_compile_file()` compileert zonder het bestand uit te voeren – zo voorkom ik dat Bootstrap-code wordt uitgevoerd tijdens de preload-fase.

Bijzonderheden van het framework

In Symfony combineer ik preloading met het opwarmen van de cache: eerst bouw ik de container en de routecache op, daarna compileer ik stabiele kernklassen. Proxies en de gegenereerde container zelf laat ik buiten beschouwing, omdat hun bestandsnamen en inhoud per build kunnen veranderen. In Laravel geldt iets soortgelijks voor config-, route- en view-caches: ze helpen bij het opstarten, maar zijn vanwege frequente wijzigingen geen goede kandidaten voor preloading. WordPress profiteert ervan als ik de hot-paths van grote plug-ins (CPT-registratie, shortcode-parser, query-utils) selecteer, zonder de volledige vendor-map op te halen.

Veiligheid en rechten

Omdat het preloaden bij het opstarten van FPM onder de gebruiker `opcache.preload_user` plaatsvindt, zorg ik ervoor dat deze gebruiker leestoegang heeft tot alle bestanden die vooraf moeten worden gecompileerd. Ik preload alleen ondertekende, gecontroleerde code uit het build-artefact. Experimentele of niet-geteste pakketten horen niet thuis in de preload, aangezien één fout de hele pool uit balans kan brengen. In multi-tenant-scenario’s scheid ik preload-scripts per pool om lekken tussen projecten te voorkomen.

Diagnose en monitoring

Voor het gebruik heb ik snelle controles nodig:

  • phpinfo(): Geeft aan of preloading is ingeschakeld en welk bestand is ingesteld als opcache.preload.
  • opcache_get_status(): Geeft het geheugengebruik, de in de cache opgeslagen scripts en het verspilde geheugen weer; ik controleer met name het resterende vrije geheugen in MB en het aantal versnelde bestanden.
  • Logboeken: Het preload-script kan een kort bericht over het slagen van de uitvoering in het foutenlogboek schrijven; bij fouten zie ik daar problemen met paden of rechten.
  • Metriek: Ik houd de TTFB, de CPU-belasting en de 95e en 99e percentielen van de responstijden vóór en na het opnieuw opstarten in de gaten om regressies in een vroeg stadium op te sporen.

Typische struikelblokken

  • Opnieuw laden versus opnieuw opstarten: Een FPM-herladen Dat is niet voldoende om de wijzigingen in de preload door te voeren. Ik ben van plan de pool helemaal opnieuw op te starten.
  • Tekort aan opslagruimte: Als opcache.memory_consumption te laag is, verdringt OPcache normale scripts of weigert het nieuwe vermeldingen. Ik reserveer ruim en controleer na de opwarmfase hoeveel buffer er nog over is.
  • Te groot aanbod: Een volledige vendor-preload neemt meer geheugen in beslag, maar verhoogt zelden de hit-ratio. Ik blijf selectief en meet de resultaten.
  • Bijwerkingen bij het vooraf laden: Sluit nooit bestanden in met globale code die databaseverbindingen tot stand brengt of afhankelijk is van omgevingsvariabelen. Ik gebruik opcache_compile_file() in plaats van require.
  • Inconsistente paden: Relatieve paden kunnen in container- of chroot-omgevingen niet meer werken. Ik werk uitsluitend met absolute paden.

Configuratie van containers en orkestratie

In containers begint het preloading-proces bij elke nieuwe pod/container opnieuw. Dat is goed voor de consistentie, maar kan de eerste minuut vertragen. Ik los dit als volgt op:

  • Readiness-test: De pod geeft pas het signaal „ready“ af als het preload-script is uitgevoerd en de OPcache stabiel is gevuld.
  • Verzoek om opwarming: Na het opstarten stuur ik gerichte verzoeken naar hot-endpoints om ook niet-vooraf geladen, maar veelgebruikte paden te initialiseren.
  • Een afgeremde rolling update: Kleine batches voor nieuwe pods, zodat niet alle instanties tegelijkertijd een koude start ondergaan.

Rollback en noodplan

Als een wijziging in de preload problemen veroorzaakt, wil ik deze snel ongedaan kunnen maken:

  • Preload-script met versienummer: Elk buildnummer verwijst naar een gedefinieerde preload-versie.
  • Snelle schakelaar: Ik heb een configuratievariant klaarstaan die opcache.preload tijdelijk uitschakelt, totdat de oorzaak is opgehelderd.
  • Een gerichte doorstart: Eerst kleine pools of een Canary-knooppunt, daarna de overige instanties gefaseerd.

Wat preloading niet oplost

Preloading versnelt het opstarten van PHP, maar is geen vervanging voor database-optimalisatie, het cachen van HTTP-responsen of asynchrone processen. Als externe diensten of query's de meeste tijd in beslag nemen, heeft preloading slechts een beperkt effect. In dergelijke gevallen geef ik prioriteit aan het optimaliseren van query's, responscaches en op wachtrijen gebaseerde workflows – preloading dient dan als aanvulling op het totale systeem.

Realistische verwachtingen per projectfase

  • Greenfield/Vroege ontwikkeling: Ik zie vaak af van preloading in lokale omgevingen, zodat ik wijzigingen kan zien zonder opnieuw op te starten. Op de staging-omgeving test ik selectief.
  • Feature-Freeze: Preloading werpt nu zijn vruchten af – bundel stabiele kernmodules en waarborg de streefwaarden voor TTFB en RPS met belastingstests.
  • Gebruik op lange termijn: Eens per kwartaal controleer ik of de preload-lijst nog steeds aansluit bij de hot-paths. Nieuwe modules worden pas na meting toegevoegd.

Korte overzichten voor snelle PHP 8-projecten

Preloading toegevoegd OPcache ideaal, omdat het centrale klassen en functies bij het opstarten van het proces permanent beschikbaar stelt. In grote projecten verlaag ik hiermee de autoload-kosten, het aantal bestandstoegangen en de parsing-inspanning, waardoor de TTFB vaak met 5–15 % daalt. Bij API- en webshop-workloads stijgt de doorvoer soms met 30–50 %, zolang de database en externe diensten dit kunnen bijhouden. De grootste winst boek ik met een duidelijke selectie, nette OPcache-parameters, tests onder belasting en geplande herstarts. Wie deze punten ter harte neemt, haalt het maximale uit PHP 8 haalt consequent meer snelheid uit het systeem en houdt de responstijden betrouwbaar laag, zelfs bij pieken.

Huidige artikelen

WordPress-server met Redis Full-Page-Cache voor snelle laadtijden
Wordpress

Redis als full-page-cache in WordPress: beperkingen en mogelijkheden

Redis Full-Page-Cache versnelt WordPress door volledige pagina’s in het werkgeheugen op te slaan. Ontdek hoe de cache werkt, welke beperkingen er zijn en hoe je het focuszoekwoord ‘redis full page cache’ optimaal kunt benutten in je configuratie.