Basic personligt

Daniel Brahneborgs blogg

Teknisk utveckling?

Kan Moore’s Law vara vänlig att börja fungera igen? På sätt och vis är det skönt när inte klockfrekvenser och minnesstorlekar flerdubblas varje år, så man inte behöver bry sig om att köpa nya datorer i tid och otid, men nu börjar det bli besvärligt.

Jag gillar min lilla elvatums MacAir, men skärmens upplösning är patetisk (1366*768). Så kom Apple med en ny MacBook nyss, som både var ungefär lika stor, märkbart lättare, bättre upplösning, men till och med långsammare cpu mot den jag har. En satans massa pengar för i praktiken bara några fler pixlar? Nope. Någon gång i höst skulle det möjligen kunna komma en ny MacBook Pro, men vad det skulle innebära återstår att se.

Så gick fodralet till min surfplatta sönder lite extra mycket idag. Tyvärr är plattan tre år gammal, så det finns inga nya skydd att köpa längre. Kanske dags att uppgradera till en nyare modell? Nej, för de plattor som finns i samma storlek har ungefär samma specar. Någon cpu-kärna till, någon tiondels gigahertz mer, men samma batteri och upplösning. Kortläsare, men utan trådlös laddning. Meh.

Vibrationen på min mobil verkar ha slutat fungera, men där har det inte heller hänt så mycket. Nu när jag testade en gång till för säkerhets skull, funkade det klockrent. Vaffan? Samtidigt har den börjat göra slut på batteriet lite väl snabbt, men eftersom batteriet inte är utbytbart är det inte så mycket att göra åt. Dvs mobilen som helhet funkar, men en uppgradering till någonting med en märkbar förbättring här och där skulle vara välkommet. Samtidigt krävs ett mikroskop för att hitta några relevanta skillnader mellan min nuvarande LG G3 och årets G5.

Nu ska jag googla “ilandsproblem”.

June 26th, 2016 Posted by Daniel Brahneborg | blogg | no comments

Matte och pedagogik

Det där med att när ens senaste verktyg är en hammare så ser alla problem ut som spikar, är någonting jag försöker upphöja till konstform. Min senaste hammare, om någon mot förmodan har missat det, är testning. Eller faktiskt mer och mer, de matematiska modeller som ligger bakom, där testning är en tillämpning av dem. Mer specifikt: grafteori och kombinatorik.

Om ens program implementerar en statemaskin kan man använda state och edge coverage för att se hur stor del av programmets logik man testar. Om de olika delarna av indatat är oberoende av varandra kan man använda base choice (man börjar med neutrala värden på allt, och så vrider man på en ratt i taget) för att metodiskt säkerställa att alla värden hanteras korrekt. Där det finns beroenden kan man istället använda combinatorial testing, där man väljer ut en minimal delmängd av “alla med alla med alla”.

Det som slog mig häromdagen är att det här är ju faktiskt exakt så som bra instuderingsuppgifter fungerar. Först får man en enkel grundsituation. Sedan ändrar man till imperfekt. Periodisering över två år istället för allt på en gång. En matris där bara en rad kan transformeras bort. En justering i taget, klockren base choice. En av de saker som jag felrapporterade med ekonomiboken jag läste förra våren var att när de gick igenom aktier och emissioner, var övningarna på både nyemission och fondemission samtidigt, vilket gjorde att jag aldrig fick bra koll på dem (och därmed inte godkänt på just den delen på tentan).

När base choice-testerna är klara, kan man då gå vidare till combinatorial, och skruva på fler parametrar i taget. Att en eventuell tenta på slutet innehåller nya kombinationer gör då ingenting, eftersom man ändå har gått igenom alla relevanta varianter. Historia å andra sidan lämpar sig förmodligen bättre för state/edge-coverage på dess mindmap-graf.

Frågan blir då: hur mycket av det här är redan undersökt? Alldeles för många av de övningsuppgifter jag har sett under åren känns mest som exploratory testing, a.k.a. “klicka sig runt på måfå”. Är en sådan här mer matematiskt baserad lista på övningar använd någonstans? Finns det några forskningsresultat? Det finns ju viss risk för vidare studier i mitt liv, så jag har ju gott om tillfälle att testa det här på mig själv om inte annat.

June 24th, 2016 Posted by Daniel Brahneborg | blogg | no comments

Studier

Ju mer jag tittar på och förstår av min lista på avklarade kurser, desto mer förvirrad blir jag. Både ämnen, nivåer och antal är så knäppa att jag borde skämmas. Att nivåerna helt har ändrats mellan när jag började plugga och nu, gör det i och för sig inte enklare. När jag började plugga fanns nivåerna A-E, som var och en representerade en termin. För en fyraårig magisterexamen skulle man då ha 30 poäng (dvs en termin) på varje nivå mellan A och D, plus lika mycket till av andra ämnen. Jag har länge trott att av de återstående 120 poängen ska 60 poäng vara inom ett sidoämne, matte i mitt fall, och 60 poäng helt valfritt (hej finska, evolutionsbiologi osv).

Så tittar jag på mina kurser, och där ser det väldigt annorlunda ut. 30 poäng A-data i och för sig, heja mig. Fast sedan bara 21 poäng B. Hur har jag då fått behörighet till massa kurser på högre nivå? Sedan har jag 60 jäkla poäng på C-nivå. Därefter blir det fint igen med 30 poäng på D-nivå, plus 30 poäng för D-uppsatsen. Nackdelen med alla de där C-poängen, är att jag då bara hann med 45 poäng matte, nästan allt på A-nivå. Det är inte alls samma sak som 60 poäng varav hälften A och hälften B. SÅ mycket matte kan jag i alla fall.

Numera har de ändrat lite, så att A-C räknas som grundnivå, dvs upp till en kandidatexamen, och D-E är avancerad nivå för magister och master. Som tur var så räknades 3 av C-kurserna nu som avancerade, vilket gjorde att jag faktiskt har just 30 poäng på avancerad nivå istället för bara 7,5.

För den masterexamen som jag siktar på att nå fram till någon gång nästa år, behövs utöver magisterexamen 15 poäng till på avancerad nivå och 15 poäng uppsats/exjobb, samt 30 poäng valfritt. Vårens kurs i testning gav mig 7,5 poäng, och 22,5 poäng av de där 30 jag har nu jobbat ihop (affärsredovisning, Androidprogrammering, evolutionsbiologi). Att jag kommer ha 216 poäng data av 300 gör förhoppningsvis ingenting, eftersom det inte gjorde något att jag hade nästan 180 poäng av 240 för magistern.

Problemet är det där med matten. Jag har länge tänkt att eftersom jag alltid har sagt att programmering och matte är samma sak, och jag redan har massa mattepoäng, borde en mattekandidat vara ett överkomligt nästa steg. Tyvärr skulle det alltså kräva 22,5 poäng B-kurser, 15 poäng C-kurser och 15 poäng C-uppsats innan jag var klar. De övriga 90 poängen kan jag återanvända från mina andra kurser, vilket är väldigt praktiskt. På kvartsfart, med en kurs på 7,5 poäng per termin, har jag då att göra i 5 terminer, plus en för uppsatsen (som jag antar inte skulle kräva ett helt år). Tre år är lång tid, för något jag trodde skulle vara nästan gratis. Fan också.

Edit: Med samma logik som för datapoängen kanske det skulle räcka med 15 poäng B-matte, men det är ändå 4 hela terminer plus ett exjobb.

Hade jag varit lite intelligentare och mer strategisk, skulle jag ha valt bort hälften av de där poängen i C-data och lagt dem på B-matte stället. Jag hade fortfarande haft tillräckligt med poäng för en dataexamen, men då hade det räckt med 30 poäng C-matte för att få ihop en kandidat nu. Någon får gärna uppfinna en tidsmaskin så jag kan åka tillbaka till 1990 och visa det här blogginlägget för mig själv.

Alternativet att sluta plugga efter masterexamen är inte med på skalan. Det finns minst två alternativ till, men vilka de är får vänta till ett senare inlägg.

Edit 2: Efter en kontroll på antagning.se såg jag att jag faktiskt hade läst en C-kurs i matte också, “Abstrakt Algebra”. Med bara 7.5 poäng på B-nivå i ryggen, plus att den kursen nu dessutom ligger på avancerad nivå, hamnade den på listan med kurser jag hoppade av.

June 18th, 2016 Posted by Daniel Brahneborg | blogg | no comments

Multitrådad statemaskin

Statemaskiner (ja, det heter säkert “tillståndsmaskiner” på svenska, men datorer ska prata engelska och det där ordet blir alldeles för svenskt) har länge varit ett av mina favoritverktyg för att implementera komplexa beteenden. En sådan består av ett antal tillstånd varav ett är det man startar i. Varje indata gör att man flyttar till ett annat tillstånd. Indatat kan vara ett tecken, en händelse som en nytt inkommande data, eller något annat. I varje tillstånd vet man exakt vilket indata som är giltigt, och när indatat tar slut är det enkelt att veta om sekvensen var ok genom att kolla om tillståndet man är i är godkänt som sluttillstånd.

I en multitrådad värld blir det lite mer komplicerat. Till att börja med krävs en mutex, så att man inte får flera trådar som pillar på statemaskinens fält samtidigt. Lämpligen har man då en handle_event(), som kollar vilket tillstånd man är i och uppdaterar maskinen till dess nya tillstånd, allt inom en låst mutex.

I vissa övergångar ska det hända någonting. Socketar ska öppnas eller stängas, data ska hämtas eller skickas iväg, eller vad det nu är. Inga problem, det är ju bara att lägga in det i handle_event? Nej, för vissa saker kan ta lång tid att utföra. Om det görs av handle_event, kommer det ske i den tråd som triggade eventet, och låsa den tråden så att den inte kan göra någonting annat.

Istället kan man då ha en loop i en separat tråd, som bara anropar statemaskinens run() tills den har nått ett sluttillstånd. Eventet “connect” flyttar maskinen till tillståndet “do_connect”, och när run() körs nästa gång, kommer den utföra uppkopplingen. När den är klar, går den vidare till tillståndet “connected”. På det sättet görs uppkopplingen bara en gång, och av rätt tråd. Å andra sidan gör det att andra trådar som vill skicka något event till den här statemaskinen kommer stanna tills uppkopplingen är klar, och så är vi tillbaka ungefär där vi började. Fast bara nästan.

Här kan man använda asynkrona funktioner istället, som antingen kan polla med jämna mellanrum eller få en callback från när de är klara. När anropet är gjort, stannar man i ett väntetillstånd tills dess att callbacken kommer. Frågan är vad man gör i den tråden under tiden. Den kan ju inte anropa run() hela tiden, för då har vi en busywait som stjäl cpu i onödan.

Den enklaste lösningen på detta är en “condition variable”. Statemaskinens tråd (den som anropar run-funktionen) hänger då på en sådan när den inte har något bättre för sig. När det kommer ett nytt event, avslutas handle_event med att väcka den som väntar på variabeln (dvs run-tråden). Det listiga är att anropet som gör att run-tråden börjar vänta, samtidigt släpper maskinens mutex, för annars skulle ju de andra trådarna inte komma in i handle_event. Efteråt är mutexen låst i run-tråden igen. I handle_event får man då: lock, update state, signal cond, unlock.

I run är det lockande att göra ungefär likadant: lock, check state, do stuff, kanske wait on cond, unlock. Symmetriskt och bra. Tyvärr fungerar det inte, eftersom det där “do stuff” kan resultera i ett man postar något event tillbaka till sig själv. Det går inte, eftersom maskinens lås redan är taget. Alltså flyttar man allt “do stuff” till efter “unlock”. Låset är då taget bara under den tid det tar att tilldela en variabel eller två, och sannolikheten blir exakt noll att låset är inblandat i någon form av deadlock.

En annan lösning, om huvuddelen av ens event kommer via sockets, är att använda något som libevent. Istället för att hänga på en cond, hänger man då i epoll eller liknande. Tyvärr går det inte att hänga i epoll och på en cond samtidigt, så för att handle_event ska kunna väcka huvudtråden använder man en intern pipe. Huvudtråden väntar på sina vanliga sockets och den här pipen, och om andra trådar vill att maskinen ska vakna så skriver de en byte på pipen efter att ha uppdaterat maskinens tillstånd. Maskinen vaknar då, läser det nya datat, gör det som behövs för dess nya tillstånd, och så hänger den igen.

June 5th, 2016 Posted by Daniel Brahneborg | blogg | no comments

|