Onlangs werd ik bij een project gehaald dat last had van ernstige performanceproblemen. Pagina’s die in het oude ASP “Classic” systeem in enkele seconden getoond werden deden er in de nieuwe ASP.NET applicatie 20-25 seconden langer over. De eerste reactie van de klant was dan ook dat de performance van ASP.NET slecht is. Helemaal ongegrond is die reactie niet.
ASP.NET is beter dan ASP
Natuurlijk is de performance van ASP.NET an sich niet slecht. Door allerlei slimmigheden is de performance van ASP.NET juist uitermate goed, zeker in vergelijking met ASP “Classic”. ASP “Classic” heeft namelijk twee nadelen als het gaat om performance: het wordt uitgevoerd door een interpreter en het is gebaseerd op Single Threaded Appartment (STA) threading, dat eigenlijk alleen geschikt is voor desktop applicaties. ASP.NET daarentegen is (JIT) gecompileerd en volledig multi-threaded. Hoe kan het dan dat een ASP.NET applicatie die functioneel gelijk is aan z’n voorganger in ASP “Classic” zoveel langzamer is? Dat komt vooral omdat ASP.NET ons lui maakt
ASP.NET maakt ons lui
In ASP.NET wordt van alles voor ons verzorgd, zoals het behoud van waardes in inputvelden en het invoegen van JavaScript op plaatsen waar we dynamische functionaliteit wensen. Voor de gemiddelde applicatie is dit allemaal heel handig en zit het ons niet in de weg. Maar als we de extremen opzoeken, dan kan dit soort handigheid enorm in de weg gaan zitten. Zo ook in het voornoemde project. Als iemand de moeite had genomen om te kijken naar de grootte van de HTML, dan had men meteen gezien dat daar iets niet helemaal goed ging. De pagina’s waren gemiddeld 6-7 keer zo groot als in ASP “Classic” en konden tot ca. 7 MB (!) groot zijn. Die HTML moet ten eerste gegenereerd worden, maar moet vervolgens ook nog eens over het netwerk en dat kan even duren, zelfs in een intern netwerk.
Voor een goede performance moet je de moeite nemen
Ik zei net “als iemand de moeite had genomen” … en dat is precies waar het bij performance meestal om draait. Je moet de moeite nemen om vooraf na te denken wat een bepaalde programmeeropdracht voor performancegevolgen kan hebben. Je moet de moeite nemen om performance te testen. Je moet de moeite nemen om je testresultaten te analyseren en om te zetten in verbeteringen. 90% Van de performanceproblemen (en dus verbeteringen) zijn eenvoudig op te sporen en vaak ook nog eens eenvoudig op te lossen. De onderstaande tips kunnen je hierbij helpen.
90% van de performance problemen zijn eenvoudig op te sporen
Tip 1: Doe altijd een performancetest
Een performancetest doen is eigenlijk helemaal niet zo moeilijk. Er zijn verschillende tools die je hierbij kunnen helpen. Een oude favoriet is de Web Application Stress Tool (WAS) van Microsoft (zie http://support.microsoft.com/kb/313559). Hiermee kun je een script maken dat requests afvuurt op een webserver. Om het makkelijk te maken kun je een script “opnemen” in je browser.
Tip 2: Gebruik Performance Monitor
Met Performance Monitor kun je sleutelwaardes van je webserver in de gaten houden. Voor ASP.NET zijn er allerlei zaken die je met Performance Monitor in de gaten kunt houden, zoals Request Wait Time en Requests Queued. CPU- en geheugengebruik geven vaak ook al veel inzicht. Heeft de CPU vrijwel niets te doen, maar is de performance erg slecht, dan is de thread pool waarschijnlijk leeg en staan veel threads te wachten op I/O operaties. In dat geval zou je eens kunnen kijken of Async Pages een oplossing zijn.
Tip 3: Maak handige meetpunten in je applicatie
Er zijn verschillende manieren om meetpunten aan te leggen. Je kunt met Windows Management Instrumentation (WMI) zelf performance counters maken die je kunt bekijken met Performance Monitor (zie http://www.ondotnet.com/pub/a/dotnet/2003/04/07/wmi.html). Ook kun je een debug log aanleggen met log4net (zie http://logging.apache.org/log4net/) of een van de vele andere logging bibliotheken. Met Enterprise Library (zie http://www.codeplex.com/entlib) kun je beide. De vraag is dan natuurlijk wat goede momenten zijn om te loggen en wat je dan moet loggen. Hiervoor is het handig om de levenscyclus van een request te kennen. Door te meten bij Application_BeginRequest en Application_EndRequest kun je precies bepalen hoe lang een request bezig is om van begin tot eind afgehandeld te worden. Wil je dit alleen weten van een pagina, dan moet je loggen op Application_PreRequestHandlerExecute en Application_PostRequestHandlerExecute.
Het is ook altijd goed om te kijken hoe lang het renderen van de HTML duurt en daarvoor kun je aan het begin en eind van de Render methode van de pagina loggen. Tot slot kun je de grootte van de pagina opvragen in Application_EndRequest met HttpContent.Current.Request.ContentLength, hoewel je dat uiteraard ook zelf in een browser kunt bekijken.