Thursday, December 6, 2007

Suboptimering

Satt häromdan och peer-reviewade lite kod. En kollega hade gjort ungefär följande:

(om du inte är intresserad av detaljerna så räcker det med att sammanfatta med 'komplex och felbenägen', så kan du scrolla ner och läsa resten)
  • Skapat en klass som hette typ ValueCache vars syfte var att cacha 'semikonstanta' värden från databasen för att undvika databasåtkomster
  • ValueCache:n instansieras i class-loading-time, varför en speciell mekanism fanns implementerad för att undvika att den läste från databasen vid initiering, eftersom risken fanns att db-connection poolen inte alltid kunde antas vara igång vid den tidpunkten
  • Förra punkten innebär ju en risk att cachen är tom första gången nån frågar efter ett värde, så därför fanns en check som initierade cachen från databasen om den var tom
  • Cachen har en time-to-live efter vilken hela cachen läses om
  • Eftersom man då ibland joxar med hashmappen medan andra delar av systemet potentiellt läser samtidigt, så var anrättningen kryddad med synchronized-block till höger och vänster
  • En freakish detalj var att cachen själv kunde slå av sig om den inte funkade nåt bra. Om man ändrar ett value (som cachades i cachen själv) i runtime, så revertar cachen till att gå direkt mot databasen (dvs precis som innan tjogtals icke-triviala kodrader skrevs, med den skillnaden att alla trådar potentiellt måste köa i synchronized-blocken som lagts till)

Jag stirrade fåraktigt på koden som hade vuxit till en Behemoth från en i grunden (one could argue) sund tanke att cacha lite data för att minska databasåtkomster.

Testfallen var enkeltrådade (eftersom det ju är krångligt att skriva bra multitrådade testfall) vilket ju liksom förfelar hela syftet - det är ju först i en lastad multitrådad miljö cachen skulle kunna ha en positiv effekt och också då den visar om den inte funkar som den ska.

Här satt jag nu och tittade på en lösning på ett problem som inte fanns. Våra eventuella prestandaproblem kommer (och det vågar jag sätta en tusing på) inte bero på slagningar mot en tabell med namn-värde-par på ett par tjog rader (med index, no less).

Det vi snarare jobbar med prestandamässigt är att få IIS att serva content i tillräcklig fart vid hög last samt några queries som inte har ett jota med valuecachen att göra.

Min gissning är att (om vi inte rycker koden) och går live med lösningen, fortare än attan får använda avstängningsmekanismen och sen ha en bara lite sämre lösning än vi hade innan.

Koden riskerar att ligga kvar, lika död som en fallen gnu på stäppen. Alltför complex-looking för att nån annan, inte insatt, ska våga ta bort den eller ändra nåt (tar man bort den så kompilerar ju inte testfallet - hu så läskigt).

Slutsats (och det är ju en gammal sanning som verkar ha svårt att sjunka in hos folk):

  • OPTIMERA INTE
  • OM DU MÅSTE OPTIMERA - OPTIMERA RÄTT SAKER
  • OPTIMERA BARA SAKER SOM ÄR PROBLEM PÅ RIKTIGT OCH DÄR DET FINNS MÖJLIGHET ATT MÄTA INNAN OCH EFTER FÖR ATT SE ATT OPTIMERINGEN VERKLIGEN OPTIMERAT NÅT

Det finns en massa duktiga erfarna programmerare därute som räknar bytes och optimerar sina små uppslagningar i sina egna hemkokta datastrukturer med egna implementationer av B-träd.

Helt i onödan. Till gagn för ingen....

Caveat 1: Cachning funkar aldrig felfritt första gången du sjösätter det

Caveat 2: Komplex kod kan vara kul att skriva, men det är alltid den enkla lösningen som blir elegant

1 comment:

Daniel Brahneborg said...

Själv cachar jag av samma anledning just nu hellre rendrerade html-sidor än find_by_id-anrop. När render tar 50% och databasen 25% är valet inte så svårt.

Kul blogg för övrigt. :)