PHP performance testen

Door AtleX op vrijdag 7 december 2007 16:53 - Reacties (1)
Categorie: Development & code, Views: 3.417

Al zolang ik door middel van PHP sites in elkaar draai, wat ik inmiddels al een jaar of 4 doe, worstel ik met een probleem. PHP is namelijk zeer lastig te benchmarken en te profilen. Verschillende programmeertalen bieden profilers, soms zelfs standaard in de IDE.

Bij PHP wordt het vaak opgelost met wat leuke statistiekjes onderaan de pagina of, zoals hier op Tweakers, in de statusbalk. Dat is leuk, maar als de query counter zegt dat er 23 queries zijn uitgevoerd zegt me dat nog niet welke het langzaamste was.

In de loop der jaren heb ik een nogal basic performance test ontwikkelt. Ik heb een servertje, eerst een oude P3 600 met 256MB geheugen en inmiddels een P3 1GHz met 512MB SDRAM, waarop ik een standaard Debian installatie heb met een recente 2.6 kernel. Daarboven op heb ik PHP, Apache, MySQL en lighttpd draaien. Voor elke keer dat ik deze server als testbak gebruik wordt deze software geüpdatet. Als client gebruik ik een VM in VMware Server met 64MB geheugen, daarin heb ik een absoluut minimale Debian installatie staan die door middel van http_load kan benchen.

Het voordeel van http_load is dat je een file op kunt geven met de URL's die bezocht moeten worden. Deze URL's worden random opgevraagd, door een instelbare hoeveelheid gelijktijdige connecties gedurende een instelbare hoeveelheid tijd. In deze file zet ik buiten alle pagina's ook de CSS- & JS-bestanden en de afbeeldingen.Vaak worden deze vergeten, maar het vereist toch een, weliswaar klein, stukje werk van de server.

De benchmark

Per project bepaal ik samen met de opdrachtgever wat de verwachte belasting is. De site van de gemiddelde MKB-er heeft dusdanig weinig belasting dat een benchmark niet eens nodig is, vooral ook omdat het gemiddelde serverpark van de een hoster ruim voldoende overcapaciteit heeft om eventuele drukte een keer op te vangen.

Mijn huidige schoolproject is wel een voorbeeld waarbij benchmarking gewenst en noodzakelijk is. Buiten het feit dat een gedeelte van de projectgroep geen ervaring heeft met het schrijven van PHP-code die overweg moet kunnen met meer dan 20 bezoekers per uur hebben we ook nog eens geen controle over waar de applicatie uiteindelijk gaat draaien. Daarom hebben we in overleg met de 'klant', de IVA in dit geval, bepaald wat de verwachte belasting van onze applicatie zal zijn.

De IVA verwacht dat ongeveer 120 studenten tegelijk gebruik zullen maken van onze applicatie, maar door de aard van het programma zal de gemiddelde tijd op een pagina best lang zijn. Daardoor hoeft hij slechts overweg te kunnen met slechts zo'n 5000 pagina's per uur. Wel zijn er een aantal bijzonder zware pagina's, die sterk afhankelijk zijn van de database.

De eerste run, van 20 minuten met 10 parallelle connecties, is een bogus run. Daarmee geef ik de server de tijd om z'n cache op te bouwen. Daarna doe ik een echte run, 120 minuten lang, met de helft van de verwachte hoeveelheid gelijktijdige connecties. In dit geval dus 120 connecties, aangezien er bij normaal gebruik maximaal (120*2 connecties per browser) 240 connecties gelijktijdig zijn. Terwijl deze test draait gebruik ik een derde client om als een normale bezoeker de website te gaan gebruiken. Met tools als Firebug & YSlow bekijk ik per pagina wat de laadtijd is.

Zolang deze onder de 1,5 seconden blijft is het goed, de server staat immers onder zware druk met honderden requests per seconde die niet alleen de webserver vrij zwaar belasten maar ook de database. Dat is veel meer dan de site uiteindelijk moet verwerken. Pagina's die bij deze test traag laden worden met ApacheBench ook nog eens 100x opgevraagd. De gemiddelde tijd daarvan is leidend.

Uiteindelijk komt er een lijstje uitgerold met alle pagina's gesorteerd op hun laadtijd. De langzaamste pagina's worden door middel van mijn PHP profiler eens nader bekeken. Deze profiler, zelf ook in PHP geschreven, kan ingevoegd worden in de code, waarna de profiler per gedeelte kan zeggen hoeveel miliseconden het duurde, en hoeveel geheugen er gebruikt werd. Hierdoor kan er exact bekeken worden welke code er (te) traag is, waarna er eventueel optimalisaties doorgevoerd kunnen worden.

Op deze manier kan het binnen 2 uur duidelijk zijn wat de ergste bottlenecks in onze applicatie zijn. Hierdoor zijn we vrijwel verzekerd van een aardig snelle applicatie, die in ieder geval op gebied van snelheid voldoet aan de eisen van de gebruiker.

Nadelen zijn er ook, het is erg, erg, arbeidsintensief, en wat nu goed werkt met de test database kan over 2 jaar toch een serieus probleem hebben met een veel grotere database. Helaas is dat nu niet te testen, aangezien we de tijd missen om een database op te bouwen met voldoende testgegevens. Daar tegenover staat dat ik nu een antieke server gebruik om te testen, en die ook nog eens veel zwaarder belast. Elke redelijk moderne server met in ieder geval meer geheugen zal de verwachte 5000 hits per uur op z'n sloffen kunnen afhandelen.

Volgende: mysql_fetch_array() vs. mysql_fetch_object() 12-'07 mysql_fetch_array() vs. mysql_fetch_object()
Volgende: De traagheid van count() in een loop (2) 12-'07 De traagheid van count() in een loop (2)

Reacties


Door Tweakers user TD-er, zaterdag 8 december 2007 09:38

Ik kende die http_load niet. Voor die enkele keer dat ik een webpage heb lopen benchmarken, gebruikte ik Apache Bench (ab).
Maar daarbij kun je (voor zover ik weet) geen lijst van webadressen opgeven, al zou je natuurlijk een paar ab-processen tegelijk kunnen draaien.

Reageren is niet meer mogelijk