Flinke performancewinst door aanpassing PHP

Afgelopen dinsdag heeft Byte een kleine wijziging doorgevoerd aan de huidige PHP5 installatie op alle web- en applicatieservers. Deze wijziging is uitgebreid getest in onze testomgeving, en is ontwikkeld om een flinke performancewinst te boeken. Uit testen bleek dat het, afhankelijk van het gebruikspatroon van een website/webapplicatie een performancewinst van enkele tientallen procenten kon leveren. De uiteindelijke resultaten liegen er niet om …

Om de performance van onze clusters in de gaten te houden hebben we op alle clusters monitoring staan, die periodiek een aantal voor websites belangrijke snelheidskenmerken meten: databasequeries, webserverprocessor en, een van de meest bepalende, het inlezen van een groot aantal bestanden. Op dat laatste kenmerk is deze aanpassing in PHP gericht. Om de resultaten maar direct te verklappen: onze performancetest laat een snelheidswinst van 40% zien.

Fileperformance cluster 1 (lager is beter)
Fileperformance cluster 1 (lager is beter)

Deze test, c1_files_deep, opent een groot aantal bestanden in een grote directorystructuur, net als een PHP-applicatie dat ook zou doen middels include(). Onze storage is bijzonder snel en efficient, maar uiteindelijk staan de bestanden nog steeds op mechanische schijven, die in de regel het traagste onderdeel van een computersysteem zijn. Aangezien een webapplicatie als Magento bij elke hit meer dan 7000 bestanden moet openen en inlezen, is dit een dankbaar onderwerp voor optimalisaties.

Technische uitleg

Bij elk bestand dat door een PHP-applicatie ge-include() wordt, moet gecontroleerd worden of het pad naar dat bestand wel klopt. Er kunnen namelijk punten (. of ..) in het pad gebruikt worden, en er kunnen symlinks (een soort snelkoppelingen) in staan. Kort uitgelegd: ‘.’ verwijst naar de huidige directory en ‘..’ verwijst naar de bovenliggende directory. Verder kan een symlink naar een andere directory verwijzen.

Vóórdat het bestand daadwerkelijk geopend kan worden, moeten deze verwijzingen eerst gevolgd worden: het besturingssysteem moet uitzoeken welk bestand er uiteindelijk geopend moet worden. Daarvoor wordt gebruik gemaakt van de functie realpath. Een pad naar een bestand ziet er op Linux, waar onze webservers op draaien, uit als ‘/home/users/abcdeftp/abcdefg.nl/includes/file.php‘. Om het uiteindelijke doel van het pad te vinden, moet van alle onderdelen (home, users, abcdeftp, etcetera) van het pad bekeken worden of het een verwijzing is. Bekijken of het een . of een .. is is natuurlijk simpel, maar bekijken of het een symlink is is ingewikkelder. Hiervoor wordt de systemcall lstat gebruikt. Dit is echter een relatief zware operatie, zeker omdat het zo ongelooflijk vaak gebeurt. Als er voor één hit 7000 bestanden geopend moeten worden, die opgeslagen liggen op een gemiddelde diepte van 8, dan wordt er dus 56.000 keer lstat() gedaan. Voor één hit, op één site…

Dat moet efficienter kunnen!

PHP gebruikt de door het besturingssysteem aangeboden versie van realpath(), maar kan natuurlijk ook een eigen implementatie gebruiken. Dat is exact wat wij gedaan hebben. Wij hebben de realpath() implementatie van Suhosin gepakt, en hier, op basis van een aantal aannames, een aantal slimme aanpassingen op gedaan om de hoeveelheid lstat’s te beperken. Dit heeft geleid tot de snelheidswinst die in bovenstaande grafiek af te lezen is. We hebben in ruil hiervoor wel een klein beetje functionaliteit in moeten leveren, maar hiervan werd geen gebruik gemaakt. In een volgende blogpost zal dit nog toe gelicht worden.