Basic personligt

Daniel Brahneborgs blogg

Experimentet som inte bevisade relativitetsteorin

De som har läst om relativitetsteorin på fysiken kanske kommer ihåg Michelsons experiment på 1880-talet, där han mätte ljusets hastighet i två olika riktningar för att mäta hur snabbt jorden rörde sig genom etern. Han fick samma hastighet åt alla håll, alltså är relativitetsteorin sann. Typ så.

Tyvärr var det inte riktigt så enkelt. Att mäta ljusets hastighet på 1880-talet gick ju inte, det fanns inte i närheten av tillräckligt bra precision i den tekniska utrustningen. Istället skickade han ljuset i två olika riktningar, lät de mötas genom en halvgenomskinlig spegel, och undersökte om interferensmönstret ändrades. Eftersom ljus är vågor, skulle en ändring i hastighet när man roterade på hela apparaten göra att vågorna från ena hållet skulle komma lite tidigare eller senare än förut, vilket skulle göra att våg+våg skulle bli annorlunda.

Problemet är då att eftersom ljuset måste åka fram och tillbaka i respektive riktning, går det först aningen snabbare, och därefter aningen långsammare (om det där med etern stämde). På grund av algebra så blir summan trots allt inte noll, men vansinnigt liten. Därför måste experimentet göras i en källare under ett klimatkontrollerat hus, för att undvika skillnader i temperatur osv. Det måste också utföras uppe på en bergstopp, helst ute i det fria, för att undvika risken för att etern “fastnar” mellan husens väggar eller liknande. Lite “good luck, have fun” över det där.

Dessutom formulerades (jag funderade på att skriva “uppfanns”, för att ge alla som vet någonting om någonting ett totalt psykbryt) relativitetsteorin inte förrän 20 år senare. Vad den skulle ha krävt för precision eller liknande för att bli bevisad, visste Michelson därför inte. Till att börja med skulle vilket värde som helst som inte var noll varit signifikant, och dessutom struntade han i att ta hänsyn till att kanske även solen rör sig genom etern, vilket skulle gett en sådan där fram-och-tillbaka-grej till. Nya tester tre och sex månader senare hade varit extremt intressant, något han inte gjorde.

Ungefär där kommer man till nästa nivå av protest, nämligen att det varken då eller nu går att bevisa en vetenskaplig teori. Det går att ge den stöd om den gör tillräckligt bra förutsägelser, men det är allt. Bevisa dem? Nope. Det enda man kan göra är att försöka falsifiera dem. Ju sämre det går, desto bättre är teorin. Denna ganska strikta syn formulerades av Karl Popper 1934, vilket var flera decennier efter att relativitetsteorin ifråga publicerades. Principen fanns säkerligen även tidigare, men kanske inte lika tydligt.

Det är inte utan att vetenskapshistoria är lite kul.

September 14th, 2016 Posted by Daniel Brahneborg | blogg | no comments

Språkkurs?

Jag skulle gärna vilja kunna prata fler språk, och sedan en tid tillbaka står tyska högst upp på önskelistan. Visserligen läste jag det i två år i gymnasiet, men de kunskaperna är borta sedan länge. Inte för att jag vet när jag ska ha tid med att lära mig ett nytt språk, men det är ett annat problem.

Den större frågan är om det går att hitta en lämplig kurs/metod/program/app överhuvudtaget. På gymnasiet var det ett fokus på grammatik på full OCD-nivå, och med ett dåligt detaljminne gick det därför inte så bra. Jag provade programmet Rosetta Stone ett tag, men utan tillräckligt perfekt uttal och annat kom jag inte så långt.

Visst, jag förstår också att en viss nivå av korrekt grammatik behövs förr eller senare. Men kan det inte få vänta lite? Det går att förstå mänskliga språk även med ganska stora fel i ordföljd, böjningar osv. Efter tillräckligt många meningar är jag övertygad om att det där reder ut sig ändå, eftersom man då får en rejäl databas med fraser att hämta uttryck från. Ett ex lärde sig skillnaden på “en plan” och “ett plan” via filmerna om Jönssonligan. Inga “durch für gegen”-ramsor necessary. Antalet situationer där det inte går att lista ut av sammanhanget vad som menas när enbart grammatiken är lite skev, känns väldigt begränsat. Det går alltid att fråga i så fall.

Om mitt mål till att börja med helt enkelt är att kunna turista på ett vettigt sätt i ett tyskspråkigt land utan att behöva använda sambon som tolk, inte att jobba som korrekturläsare på Die Welt, vart vänder jag mig då? Det går ju alltid att flytta till typ Berlin och köra “French Café” (eller i det här fallet kanske German Bierstube)-metoden, men det känns lite onödigt besvärligt.

August 31st, 2016 Posted by Daniel Brahneborg | blogg | no comments

Ny disk

Jahapp, då är hårddisken på servern som kör RSS/Ping utbytt. I början gick det ganska bra, eftersom det fanns lediga kablar för både ström och SATA, och datorn sedan bootade fint. Den har två partitioner, och båda är backupade till en annan dator. Lite fdisk, mkfs och “rsync -av”, så var grunden klar. In i nya diskens /etc/fstab för att ändra vilka partioner som skulle användas, men så var det ju det där med Grub. Jag hatar Grub.

Jag googlade ganska mycket, och kom fram till att den “grub-install” och eventuellt en “update-grub” skulle räcka. Stäng av, ut med gamla diskens sata-kabel, omstart. Grub går igång, men stannar omedelbart med ett “error 21″. Väldigt hjälpsamt. Tydligen betydde det att den inte hittade den disk den ville ha.

Mer googling, utan resultat. Lite väl många träffar var svar av typen “men den där distributionen är ju mer än ett ÅR gammal, varför kör du sådana antika saker?”. Tröttsamt. Nya försök, fortfarande error 21. Vid något tillfälle gick inte Grub igång alls, men det löste sig när jag kopplade bort några diskar som ändå inte användes längre. Att köra chroot först, för att på det sättet tvinga Grub att bara se den nya disken, funkade inte. Inte ens med några “mount –bind” för /dev och sånt. Den letade fortfarande efter den gamla disken.

Grub har en konfigurationsfil som börjar med en stor “automatgenererad, ändra inte i den här”-varning. Alltså ändrade jag i den, och la till ett entry som sa att den skulle boota med samma kernel och grejer som vanligt, men med nya disken som root-partition. Det funkade faktiskt. Grub kom då från gamla disken, men hela filsystemet låg på den nya. En ny grub-install, reboot med gamla disken urkopplad igen, och tada! Grub gick igång från den nya disken, hittade sin konfigfil, och fick igång Linux.

Gamla disken hade fått läsfel, så MySQL-databaserna fick jag skapa om från backuper, men sedan var allt igång igen. Det hela tog bara fyra timmar, hurra. Jag gillar verkligen inte hårdvara.

Att alla diskar monterades via UUID var förresten en väldigt bra idé. Tack vare det kunde jag koppla in och ur diskar hur jag ville, utan att monteringspunkterna ändrades. Om disken hette /dev/sda eller /dev/sde spelade ingen roll, för det namnet användes ju inte ändå.

August 21st, 2016 Posted by Daniel Brahneborg | blogg | no comments

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

Testning utan droger

För ungefär 15 år sedan hamnade jag för första gången på ett företag som hade en speciell grupp människor som primärt arbetade med att testa den kod som vi programmerare producerade. Efter att ha fixat koden så att de inte längre hittade några fel, rapporterades exakt noll buggar i fält. Det var uppenbart att det inte bara handlade om slump vilket data man testade med, men längre än så kom jag inte. Jag hade länge en misstanke om att det involverade droger.

Till min framtida Master-examen behöver jag ytterligare två kurser (eller 15 hp, mer exakt) i datavetenskap på avancerad nivå, så nu under våren går jag en kurs i just testing hos MDH i Västerås. Några månader med föreläsningar, vetenskapliga artiklar och egna uppgifter har bilden klarnat rejält. Istället för att hela området är en vit yta har jag åtminstone en översiktlig karta nu.

Första pusselbiten är att se utvecklingsprojekt ur tre perspektiv. Först har man omvärldens önskemål på systemet. De formaliseras i en specifikation, som i sin tur implementeras i kod. De tre har en exakt överlappning varje gång som en enhörning och en flygande gris tillsammans får en fertil avkomma.

Nästa pusselbit är synen på program som funktioner som omvandlar indata till utdata. Att skapa testfall handlar då om att hitta bra och minimala subset av det där indatat. T.ex. brukar det vara mer intressant att skicka in -1, 0 och 1 än 2, 3 och 4. Testar man med 2 och 3, behöver man förmodligen inte testa med 4 och 5 också.

Även processen att skapa testfall kan ses som en sådan här funktion. När indatat är kod, består funktionen av verktyg som Valgrind och kompileringsvarningar. De är enkla att använda, och är en bra början. För mig är de också den lägsta nivån som är acceptabel. Program kan ha buggar, men om Valgrind gnäller så är programmet opålitligt. Då får man saker som Heartbleed.

När indatat är omvärldens önskemål blir funktionen att se hur slutanvändarna faktiskt använder systemet, hur blicken rör sig över skärmen, om det finns funktionalitet som fattas, osv. Här börjar det gränsa till psykologi och beteendevetenskap, så det är lite för långt utanför min kompetens.

Min senaste besatthet är istället området däremellan. Om specifikationerna är tillräckligt formaliserade (XML, tillståndsdiagram i UML och liknande), finns det några olika typer av verktyg man kan köra för att få ut serier med testdata. Antingen har man sedan ett annat program som tar det testdatat och matar den riktiga applikationen med, eller så har man det som protokoll för mer manuella tester. Poängen är att man får minimalt med testfall som täcker maximalt med kombinationer av indata.

Antag att man har 10 parametrar som kan vara 0 eller 1. Att testa alla kombinationer av dessa ger 1024 testfall. Med fler parametrar som kan ha fler värden, växer det där antalet snabbt. Istället börjar man med 0 överallt, så blir testfall 2-11 när man ändrar en parameter i taget till 1. På 11 testfall har man då täckt in samtliga värden av samtliga parametrar. Skapar man testfallen manuellt är det ungefär här man hamnar. Dags att levla upp.

Det finns flera verktyg som kan ge en lista med testfall där alla värden på alla parametrar minst en gång kombineras parvis med alla värden på alla andra parametrar. För 3 parametrar får man t.ex. serierna 0-0-0, 0-1-1, 1-0-0, 1-1-1, 0-0-1, och 0-1-0. För 10 parametrar blir det totalt 20 testfall. Det är fler än 11, men enormt mycket färre än 1024. Många buggar behöver bara att 2-3 variabler har en viss kombination av värden för att triggas, så effektiviteten är förvånansvärt hög. Om själva testandet kan automatiseras blir det en utmärkt testsvit att köra efter varje incheckning, varje natt eller vad som nu är lämpligt.

När kursen började sa jag till lärarna att jag ville ha nya mentala verktyg, för att kunna testa program på ett mer strukturerat sätt. Det kan man ju lugnt säga att jag har fått. Kursen pågår två månader till, men av det jag sett av det återstående innehållet är det inget som riktigt slår steget från “det kräver hallucinogena droger” till “testdata kan genereras automatiskt från kravspecifikationer”.

March 16th, 2016 Posted by Daniel Brahneborg | blogg | no comments

There and back again

Att låta ett program hoppa mellan olika programspråk var visst inte så enkelt. Från C kan man öppna filer med kompilerad C-kod och anropa funktioner där utan större problem. Funktionerna där kan använda externa bibliotek, liksom anropa funktioner i det ursprungliga programmet. Av någon konstig anledning verkar folk tyvärr inte gilla det språket, så vi ville erbjuda fler alternativ.

Jag hade kört en del Ruby, och tänkte att det kunde vara ett trevligt språk att erbjuda. Tyvärr fick jag aldrig minneshanteringen att bli rätt där, och när jag nu kollade några år senare så verkar det som om språket fortfarande är helt enkeltrådat, och inte ens fungerar som del av en multitrådad applikation.

Numera fanns även mruby, som klarade trådar. Tyvärr så använde den inte rubys normala “gem”-hantering, vilket gör den ointressant.

Det gick att anropa funktioner i Perl från C, så länge som man lät varje Perl-motor ligga i en egen tråd. Dokumentationen var ganska bra (även om det råder delade meningar om vad SvPOK() faktiskt testar), och minneshanteringen gick att få stabil. Anrop tillbaka till C var däremot en helt annan sak. Hela upplägget där bygger på att C-koden är en del av en Perl-modul, med sina egna byggregler och annat. Varianten att C-koden är en del av programmet som anropade Perl, hittar jag ingen dokumentation för. Antingen skriver man en mod_perl som anropar perl, eller så gör man en mysql-adapter. Inte båda.

Python kändes lockande, med bra api-er åt båda håll, ända tills jag såg listan med minnesläckor. Nej, tack.

För PHP hittar jag bara dokumentation för när PHP anropar C, inte tvärtom. Det lockar inte att sno källkoden till mod_php, även om den gör väldigt nära det vi vill ha. Dessutom framgår inte hur trådsäkert det är.

Det finns en reimplementation av PHP som heter PH7. Där är det enkelt att göra anrop åt båda håll, den är trådsäker, men den verkar inte ha något stöd för externa standardpaket. Allting måste därför finnas antingen i PHP-koden eller i applikationen som anropar den.

Javascript kändes också lockande, speciellt med Googles V8-motor. Den är trådsäker, man kan göra anrop åt båda håll, men så var det ju det där med externa moduler. De enda javascriptmoduler jag hittar är byggda ovanpå node.js. Att köra sådan kod skulle kräva en egen reimplementation av ungefär hela node, eller att vi skrev om hela vår app att också köras inuti node. Inget av det lockar överhuvudtaget.

Python, PH7 och V8 är onekligen en generation senare än de andra. Mycket renare API än Perl, och i V8-fallet dessutom med massor med coola features från de nyare varianterna av C++ (framför allt C++11) för att undvika minnesläckor. Det är irriterande att det verkar vara så svårt att kombinera det här med språkens övriga ekosystem, så som Perl lyckas med.

Är det något språk jag har missat, som faktiskt uppfyller alla krav? Java?

February 12th, 2016 Posted by Daniel Brahneborg | blogg | no comments

Testade Python

Ett klassiskt sätt att låta användarna göra egna tillägg till ett program är att stödja ett eller flera skriptspråk. Istället för att starta en ny process för interpretatorn som kör användarens kod, görs den till en del av huvudprogrammet. Det är så webbservrarna kör perl och php, t.ex. Jag tänkte göra detsamma nu, men för python.

För att vara säker på att mitt program inte har några minnesläckor eller klantar sig på andra sätt, testas varje ny version med Valgrind. Den hittar saker som att man läser värden innan de är skrivna, använder minne efter att det är återlämnat, och massa annat. Jag började nu med ett minimalt program som traditionsenligt bara skrev ut “Hello World”. Valgrind blev synnerligen irriterad, och skrev ut hundratals varningar. Ok, jag kanske hade missat någonting?

Jag testade med den riktiga interpretatorn, och körde samma sak där. Jo då, hundratals varningar även nu, både minne som glömts bort och som försöks återlämnas flera gånger. Det senare felet är allvarligt, för det kan göra att programmet kan krascha helt slumpmässigt. Om man råkar ha någon fil öppen så kan även innehållet där skrivas över. Det är verkligen en “all bets are off”-situation. I Unix-världen är det därför ganska vanligt att man struntar i att lämna tillbaka minne när programmet avslutas, eftersom allt ändå tas om hand av operativsystemet. Det är onödigt att flyttstäda i ett hus som ska rivas. Ändå störde det mig.

Man kan bygga python med en speciell flagga, som gör att den funkar bättre med just Valgrind. Sagt och gjort. Jo då, alla dubbla återlämningar försvann. Tyvärr fick jag då bara ännu fler varningar om bortglömt minne. Att få reda på om den läcker lika mycket oavsett vad man gör, eller om den läcker lite grand varje gång ens program körs, är inte helt lätt att få reda på. Det går säkert, men kräver större kunskap om python-källkoden än vad jag har tid och lust att skaffa mig. Att få överblick på en halv miljon rader C-kod är inte gjort i en handvändning.

Å andra sidan spelar det ingen roll. Att lämna tillbaka minne i rätt ordning är ibland väldigt svårt. Antingen glöms vissa bitar bort, eller så trampar man sig själv på tårna och lämnar tillbaka saker för tidigt. Jag har haft samma problem med min egen kod mer än en gång. Poängen är att anledningen till de problemen, är exakt alltid i samtliga fall att jag inte har haft kontroll på vad koden egentligen gör. Saker körs, men jag vet inte i vilken ordning. I ett sådant läge är det lätt att bara ge upp, eftersom man inte riktigt behöver bry sig. När programmet avslutas, kommer operativsystemet ju ändå städa upp. Om det alltid läcker lika mycket minne oavsett hur länge programmet har körts eller vad det har gjort, är det i praktiken inte ett problem.

Från mitt perspektiv är det ändå inte ok. Om problem med minneshantering kan bero på bristande kontroll, betyder det att mitt förtroende för koden minskar. Om programmerarna har problem här, har de förmodligen problem med andra saker också. Omvänt gäller också. Har man stenkoll på vad koden gör och vilken del av koden som äger vilket minne, är det en kakbit att se till att minnet återlämnas på rätt sätt och i rätt ordning.

Det hela blev ännu sämre av att flera filer måste vara skrivbara när man kompilerar python, eftersom de genereras på vägen. Ändå måste de finnas från början, annars går bygget sönder med en gång. Återigen, brist på kontroll på sakers livscykel.

Så nej, det blir ingen python just nu.

February 8th, 2016 Posted by Daniel Brahneborg | blogg | one comment

« Äldre |