For ikke så mange år tilbage kørte den software, som blev udviklet, på hardware med få CPU’er, med lidt (og dyr) RAM. Datamængderne var målt i GB og det var som oftest mere reglen end undtagelsen at svartider på applikationer var omkring et sekund. Sådan er det ikke længere. I dag har vi mulicore processorer, masser af RAM (der endda er billig), og vi flytter enorme mængder data rundt. Sidst men ikke mindst så kræves det af alle applikationer – store som små, servere eller apps, at svartiderne ligger fra mikrosekunder til millisekunder. Og applikationerne skal kunne køre 24/7 – uanset mængden af brugere eller data.
Dette kræver en hel del af os softwareudviklere. For at finde en brugbar løsning skal vi se tilbage til vores studietid (eller endnu længere tilbage i tid). På studiet lærte vi om Big O-notationen, om hvilken udførselstid en given algoritmne har, om CPU arkitektur og memory management, om synkron vs asynkron kommunikation, om låse og semaforer, og om datastrukturer. Løsningen er altså ikke at lære et nyt fancy programmeringssprog, der måske vil kunne hjælpe med at håndtere de udfordringer vi står med. Vi skal med andre ord have flere nørder og færre hipsters, som Martin Thompson sagde under sit foredrag på GOTO Aarhus konferencen i går.
Vi skal blive bedre til at udnytte de hardware ressourcer vi har til rådighed bedst muligt for at kunne udvikle applikationer der er ‘responsive‘ dvs. svarer tilbage (eller reagerer) lynhurtigt. Vi skal forstå præcist hvad der går galt, når vi kan se at der er latency i vores applikationer. Ellers kan vi ikke løse problemet. Og for at kunne forstå hvad problemet er, skal vi måle, måle og måle.
Start med at finde ud af om det er på netværket, der opstår problemer. Brug tools, der tilgår netværket direkte, fx med tcpdump, i stedet for at måle netværkslatency efter at beskederne er blevet processeret. Brug profiling værktøjer til at finde ud af hvor i koden, det går galt. Sørg for at have et continuous performance testing system. Lad i øvrigt være med at gemme performance testing til aller sidst i et projekt, lige inden softwaren skal releases. Lav performance testing løbende.
Optag og gem samtlige performance test-kørsler af din applikation, og generér et histogram over alle kørsler, der viser latency som funktion af tiden. Undersøg histogrammet og beregn percentilerne – fx giver det et meget godt indblik at se at fx 99,99% af alle transaktioner tog under x antal millisekunder. I denne sammenhæng giver det ikke mening at kigge på middelværdien eller medianen for den latency-graf der genereres, da det sjældent ligner en statistisk normalfordeling på nogen måde.
En anden vigtig pointe er, at vi skal sørge for at CPU’erne ikke er under maksimal load hele tiden. For det første er der en kølings-mekanisme – en energi-besparende funktion – i de nyere CPU’er. Hvis loaden bliver for høj, så vil CPU’en automatisk udføre instruktionerne langsommere, hvilket giver længere svartider end forventet for applikationen. Kør med max 60-70% af din CPUs ydeevne for at opnå hurtige svartider døgnet rundt.
For at opnå en bedre udnyttelse af CPU’erne (med de samme hurtige svartider) så skal vi forstå matematikken bag Queueing theory og Little’s Law.
Vi skal kende vores algoritmer og datastrukturer og vide hvilken udførselstid de har, for at kunne finde ud af hvad vores applikationers ‘service time‘ er. Vi vil gerne kunne estimere denne ‘service time‘ og det kan vi kun hvis vi har et deterministisk system. Vi vil i fremtiden også få flere CPU’er som er hurtigere, men vi vil uundgåeligt få dårligere svartider på vores applikationer, hvis algoritmerne har en langsom udførselstid.
Algoritmer og datastrukturer er kommet for at blive. Dem kan vi ikke komme uden om. Men diverse programmeringssprog, tools eller libraries kommer og går – nogle bliver og andre forsvinder hurtigt igen.
Hvis du gerne vil udvikle en responsive applikation, så gå i gang med at måle og analysere din software. Prøv i øvrigt open source værktøjet jHiccup.
Martin Thompson gav i sit foredrag eksempler på hvordan han kunne fjerne spikes i latency ved at løse eventuelle GC-issues, eller ved at se på om problemet lå i at datastrukturer blev resized på runtime, eller om der periodisk opstod cache misses.
I øvrigt er virtualisering ikke en god ide, hvis du vil opnå hurtige svartider. Et virtuelt OS tilføjer endnu et lag af systemkald, som ikke kan udføres uden at det koster mht. latency. Et virtuelt OS skal jo tilgå disken og netværket, og kunne håndtere eventuelle locks i din software (som involverer OS’et).
Martin Thompson er specialist i high-performance og low-latency computing, og jeg vil klart anbefale at få læst hans blog om mechanical sympathy, eller høre hans foredrag, eller læse om Disruptor frameworket, som han har været med til at udvikle. Også selvom man ikke selv beskæftiger sig med high-performance systemer, da alle principperne bag kan anvendes på alle typer applikationer for at få (de stærkt ønskede) hurtige svartider, mere robuste løsninger og et mere elegant design.
Martin sluttede sit foredrag af med et citat af Dijkstra: “Use abstractions to make it more precise”. Dvs. brug ikke som undskyldning, at der er et abstrakt lag oven over alt det hardware-nære, som tager sig af disse problemstillinger. Lær i stedet om principperne bag mechanical sympathy. Du behøver ikke være en ekspert for at få noget ud af det.