Tidsplan för Kalmar Triathlon, version 2

Jag känner mig optimistisk inför lördagens äventyr. Det är mycket som kan gå fel, men så länge som min utrustning håller ihop och jag inte drabbas av någon oöverkomlig skada kommer jag orka runt. Något annat är helt enkelt uteslutet. Så istället för att måla upp worst case-scenarios (jag har packat en reservslang, ett multiverktyg, några buntband och en Leatherman i sadelväskan, det borde lösa 90% av problemen som kan uppstå) funderar jag på hur snabbt det hela kommer att gå. Jag har tre tidsplaner: Den pessimistiska, den ambitiösa och glädjekalkylen, som ger en sluttid på 15, 13 resp 12 timmar. Jag har även en målsättning för nästa år på vad jag måste fixa för att komma under tio timmar — man ska ta i med sina ambitioner.

Jag gjorde en tidsplan redan i april. Efter det har jag tränat som en galning och testat hur den halva distansen känns på en riktig tävling. Sånt ger självförtroende, vilket reflekteras i den nya planen.

Nedanstående är gjort utifrån hur lång tid jag tror att ett varv i var och en av grenarna tar — Kalmarbanan fungerar som så att man simmar tre varv á ~1,3 km, cyklar tre varv á ~60 km, och springer tre varv á ~14 km. Min målsättning är att hålla ett jämnt tempo så att sista varvet går på samma tid som första.

Från detta får man sen en snittfart (uttryckt i både km/h och min/km). Märkas kan särskilt att löphastigheten är relativt låg jämfört med den i cykel och simning — även om jag tror att jag kan pinna på rätt bra på cykeln efter simturen, lär benen vara betydligt mer slitna när det är dags för löpning.

(längd,km) Glädjekalkyl Ambitiös Pessimistisk Hjorten Nästa år
Simning varv 1,287 00:31 00:35 00:40 00:27 00:22
(snitt km/h) 2,49 2,21 1,93 2,86 3,51
(snitt min/km) 24:05 27:12 31:05 20:59 17:06
Växling 1 00:05 00:10 00:15 00:05 00:05
Cykling varv 60 01:55 02:00 02:15 02:01 01:40
(snittfart) 31,30 30,00 26,67 29,63 36,00
(snitt min/km) 01:55 02:00 02:15 02:02 01:40
Växling 2 00:02 00:04 00:08 00:02 00:02
Löpning varv 14,065 01:30 01:40 01:55 01:31 01:15
(snittfart) 9,38 8,44 7,34 9,22 11,25
(min/km) 06:24 07:07 08:11 06:30 05:20

Kolumnen ”Hjorten-farten” är gjord utifrån den snittfart (och de växlingstider) jag hade på Hjorten triathlon för några månader sedan. Som synes simmade jag rätt snabbt där jämfört med vad jag hoppas på i Kalmar, men det är betydligt enklare att simma i bassäng i ordnade banor, jämfört med ute i öppet vatten (särskilt som väderleksrapporten för lördag säger åtta sekundmeters vind). Cyklingen har jag jobbat mer på och hoppas på att kunna köra något snabbare än på Hjorten även fast distansen är den dubbla, inte minst som banan sägs vara väldigt flack (och jag inte direkt är stark i uppförsbackar). Löpningen slutligen hoppas jag kunna förbättra mycket jämfört med Hjorten, men jag har i ärlighetens namn ingen aning om hur jag kommer känna mig efter arton relativt snabba mil på cykeln.

Det stora problemet på Hjortenlöpningen tror jag var att jag helt enkelt hade slut på energi, och när man ser till hur lite jag åt och drack (1½ l sportdryck, en kexchoklad och en banan, ingen kolhydratladdning innan) är det kanske inte så konstigt. Nu har jag tänkt igenom min energibudget mer ordentligt och kommer försöka sätta i mig minst 240 kcal i form av mer eller mindre snabba kolhydrater per timme (vilket ska vara den ungefärliga maxgränsen för vad kroppen kan ta upp, energimässigt). Jag kommer också starta med musklerna sprängfyllda av glykogen.

Ovanstående fartplanering ger följande hålltider:

Glädjekalkyl Ambitiös Pessimistisk Hjorten Nästa år
Simning start 07:00 07:00 07:00 07:00 07:00
Simning varv 1 07:31 07:35 07:40 07:27 07:22
Simning varv 2 08:02 08:10 08:20 07:54 07:44
Simning varv 3 08:33 08:45 09:00 08:21 08:06
Cykling start 08:38 08:55 09:15 08:26 08:11
Cykling varv 1 10:33 10:55 11:30 10:28 09:51
Cykling varv 2 12:28 12:55 13:45 12:29 11:31
Cykling varv 3 14:23 14:55 16:00 14:31 13:11
Löpning start 14:25 14:59 16:08 14:33 13:13
Löpning varv 1 15:55 16:39 18:03 16:05 14:28
Löpning varv 2 17:25 18:19 19:58 17:36 15:43
Målgång 18:55 19:59 21:53 19:08 16:58

Om sisådär 72 timmar får vi se hur det gick…

Allt du lärt dig om webutveckling är fel

Ett uttryckligt designmål för lagen.nu har varit att göra så mycket som möjligt med statiska filer. Det var en nödvändighet av prestandaskäl när tjänsten körde på en 166Mhz Pentium, och det är även idag en vettig målsättning, inte minst ur robusthets- och säkerhetssynpunkt.

Så länge som en webtjänst inte förändras på grundval av användarnas interaktion finns det inte någon strikt anledning att göra den med dynamisk teknik som ASP, PHP, CGI-script, servlets/JSP etc. Det kan förstås finnas utvecklingsmässiga fördelar; i princip alla nya tekniker för webutveckling utom möjligtvis XSLT är anpassat för dynamisk sidgenerering, så man når kanske sina utvecklingsmål snabbare med en dynamisk lösning. Men jag har ingen brådska på det viset.

Etiketteringen är dock den första funktionen som tar emot data från användaren, och har som sådan berett lite extra problem. Det första problemet jag hade att lösa var att jag ville att funktionen bara skulle vara tillgänglig för de som explicit ville ha den under en testperiod. I dynamiska system gör man oftast liknande saker med inloggningsförfaranden och sessionsobjekt, samt villkorlig kod som, beroende på inloggningsstatus, spottar ur sig lämplig HTML-gränssnittskod.

Men i en statisk lösning kan man inte göra sådant på servern. Däremot kan klienten åläggas att göra en hel del dynamiska trick! Lösningen fick bli att den gränssnittsdetalj som används för etiketteditering initialt gömdes med en style="display: none;", för att sedan, när sidan laddats klart, eventuellt göras synlig baserat på om kakan tags är satt till enabled med javascript i den här stilen (elementet heter ”comment” av hysteriska skäl):

    if (GetCookie('tags') == 'enabled')
    {
	if (document.getElementById('comment'))
        {
	    document.getElementById('comment').style.display="block";
         }
    }

Naturligtvis kan man inte använda en sådan här lösning om den detalj som man eventuellt vill gömma innehåller någon typ av hemlig information — det är trivialt för en angripare att kolla på HTML-koden för att hitta informationen.

Ett annat designmål med lagen.nu är att så långt som möjligt undvika att använda en traditionell RDBMS som MySQL, SQL Server, Postgres etc. Även här rör det sig om en önskan att hålla saker så enkelt som möjligt genom att undvika en separat körande process, som dessutom implementerar sin helt egen rättighetsmodell (vanligtvis), kräver sina egna backup- och synkroniseringsrutiner. En databasserver är, om man tänker efter, bara ett uppsvällt userland-filsystem med en väldigt bra ls 🙂

På lagen.nu jobbar jag framförallt med dokument – lagtexter och liknande, som är väldigt naturligt att representera som filer i ett filträd, snarare än BLOBar i en databas. Men även för datamodeller som traditionellt skulle representeras i en RDBMS har jag valt att använda filer – XML, närmare bestämt. Ett exempel är kopplingen rättsfall – lagparagrafer. Varje rättsfall kan referera noll eller flera lagparagrafer (inklusive i olika lagar), och varje lagparagraf kan naturligtvis refereras från många rättsfall. Istället för att ha en Verdicts-tabell, en LawParagraphs-dito, och sen en många-till-många-mappingtabell har jag ett stort XML-dokument med grundläggande uppgifter om alla rättsfall, ordnade efter lagar och paragrafer. Sen länkar jag in relevanta rättsfall under XSLT-transformationen, beskrivet här (under ”Accessing data in other documents”). XML-dokumentet innehåller redundant data, men eftersom det inte är den ursprungliga källan till informationen (det skapas av bara farten under den vanliga transformationen av rättsfallen) är det ok.

Den stora fördelen med detta är att mycket funktionalitet kan implementeras i XSLT istället för python eller SQL (vissa skulle ifrågasätta om det är en fördel, men jag tycker det har sin inneboende charm).

Etiketter har en liknande datamodell med en många-till-många-relation mellan etiketter och lagar, och även här är tagdatabasen en stor XML-fil (eller, än så länge är den ganska liten, men ge den tid).

För att ta emot den nya informationen måste jag tyvärr frångå mina principer om en totalt statisk sajt. Ett litet CGI-script tar emot formulärdatan, uppdaterar etikettdatabasen, och ansvarar för att fixa till HTML-filen: när en användare lägger till en etikett på en lag måste den statiska HTML-filen som utgör den formatterade lagen ändras för att inkludera den nya etikettexten.

Och här råkar vi på ett problem som inte finns i en helt statisk sajt: prestandakrav.

Sidorna på lagen.nu genereras vanligtvis (i sista steget) från XML till XHTML genom en XSLT-transformation från helvetet. Den gör mycket magiska saker, och den tar lång tid – den mest krävande lagtexten, Rättegångsbalken, tar strax under 45 sekunder. Det är inte OK om användaren måste vänta så länge för att sätta en tag. Istället opererar CGI-scriptet på den färdiggenererade HTML-filen genom att mmap’a den, söka sig fram till en markör-kommentar, och dumpar sedan in den nya taglistan.

För att tokfort hitta relevanta taggar för lagen i fråga använder jag cElementTree , och sen spottar jag ut HTML på gammalt klassiskt f.write("<a href='foo'>bar</a>")-manér.

Det är dock fortfarande långsamt att lägga till stora mängder etiketter på en lag, detta på grund av lite suboptimalitet på ett annat ställe som jag för tillfället helst sopar under mattan. Dessutom skalar det inte, men när jag får så mycket etiketter att det blir ett problem ska jag jubla.

Så, för att summera: Undvik dynamiska webutvecklingsramverk, och undvik relationsdatabaser. För att vara mer tvärtemot etablerad webutvecklingskutym skulle jag behöva programmera i assembler.

Gränssnittet för etikettering i sig är däremot en dynamisk HTML-festival i miniatyr. Imorgon ska jag berätta om min nyvunna fascination för den här utskällda webdesigngrenen.

Premature cliches are the root of all evil (and considered harmful)

Did you know that you can use wild cards in Google searches? Searching for ”premature * is the root of all evil” yields a lot of people quoting Knuth in an almost fanboy like fashion (though it seems that Tony Hoare is the actual source of the quote). I’d like to nominate ”Premature optimization is the root of all evil”, together with ”Goto considered harmful”, as the most tired and overrated quotes in computer science ever.

Don’t get me wrong, these quotes made a lot of sense thirty, or even ten years ago, but now that the ideas that they illustraded has been absorbed into the programming community, they do more harm than good. For example, Dijkstras paper titled ”Goto considered harmful” argued against the practices found in ALGOL and similar environments 36 years ago. Most common languages at the time had weaker support for structured programming, and the general consciousness about structuring code in the programming community was low, leading to ”spaghetti code” with goto’s all over, making it hard to figure out how the program actually worked. At that time, the condemnation of goto’s made sense.

Well, a lot have happened since that time, both language-wise and with programmer’s attitude towards the systems they’re building. ”Software ICs”, object-orientation, components, SOA… all leading to environments and programs that can grow quite large without collapsing under the weight of it’s own complexity (some say that we’ve exchanged the problems of ”spaghetti code” for that of ”ravioli code”, but that’s another discussion). When a programmer nowadays considers using ”goto”, it’s usually for situations where it actually makes sense (breaking out of nested loops, for example). Having the mantra ”Goto considered harmful” alive in the collective programming community consciousness actually makes things worse, as it discourages people for using the construct where it actually would make the program easier to read.

The quote about ”Premature optimization is the root of all evil” is more of a ”timeless” truth, but of course it’s basically a truism — premature anything, is by definition, never a good thing. The quote is rooted in a time where optimizing code would make it harder to read and maintain, and really is aimed against those micro-optimizations that make code harder to read (explicit loop unrolling, contrived program flow, unneccesary use of bitshifting and bitfields) without providing a huge performance benefit. However, this quote has been repeated so often, often in an authorative voice, that new programmers think that there’s something wrong with thinking about performance as you design and write the initial version of your program — fearing that it will lead to a design that is worse and a program that’s harder to maintain.

These fears do not match my experiences. I think it makes sense to think about performance through the design phase, particularly considering which pieces of code will be run often (I seem to recall statistics saying that 98% of a program’s running time is spent in 2% of it’s code), and what is needed to get those parts to run really fast. Of course, I don’t consider these optimizations to be premature.

I’ve seen cases where architectural descisions were made early on, without measuring performance, leading to a system that was not only slow, but basically impossible to optimize without rearchitecting. As these systems were built without much thought about performance, when the performance problems surfaced, it was basically too late. These scenarios are, in my experience, much more common than systems where optimization has made the code hard to read and maintain.

However, there are other stuff that often is done prematurely, and in my experience are a much larger source of evil — abstraction and/or generalization, things that are generally considered to be good things. New (or even experienced) programmers that are ambitious will often design overly generalized systems without being familiar enough with the problem domain to know which generalizations makes sense. Similarly, they will create abstractions through elaborate class hierarchies and interfaces with the intention that it will make the individual pieces of the system easier to understand, without realising that the abstractions get in the way of understanding the system as a whole.

Code that has been obfuscated by premature optimization attempts usually can be made more readable piece by piece, but an overengineered system with sophisticated class hiearchies that turned out not to match the problem domain is much harder to refactor into something useable. I wholeheartedly agree with this page on the original wiki that states:

It’s probably important not to create an abstract base class or an interface until you can think of at least two descendants or implementations — that you are actually going to use right now

When reading and understanding other peoples code, I find that it much more common to find it over-abstracted than under-abstracted. Maybe we need new quotes/cliches/memes to warn us of where the real risks actually are now, as opposed to where they were thirty years ago.

Next time, I’ll take apart the Fred Brooks quote ”Adding manpower to a late software project makes it later” 🙂