· · 7 min leestijd Beginner

De verloren kunst van software-optimalisatie: wat de demoscene ons leerde over duurzaamheid

Waarom we stopten met het schrijven van efficiënte code, wat de demoscene wist over optimalisatie, en hoe software bloat een milieuprobleem werd.

Een klein beige kastje draait een demo terwijl een koelkastgrote moderne machine rookt om één knop te tonen

Zo shippen de meesten van ons tegenwoordig software. Framework kiezen, een paar honderd dependencies binnentrekken, en de service staat al op een gigabyte RAM voordat hij z’n eerste nuttige regel werk heeft gedaan. Niemand profilet het. Is het traag, dan schaal je op. Compute is goedkoop, jouw tijd niet, en de rekening gaat naar iemand anders. Dat zeg ik niet als klacht. Ik heb het zelf gedaan, en bij de volgende deadline doe ik het weer.

Ooit leefde ik in een andere wereld. Uren, soms hele weekenden, zat ik een programma op hardware te persen die het “onmogelijk” aankon. Dat het dan tóch draaide was het mooiste gevoel dat ik kende. De demoscene was m’n inspiratie. Ik keek naar een intro die onmogelijke dingen deed in 64 kilobyte en zat me af te vragen hoe ze dat in hemelsnaam voor elkaar kregen.

Dat instinct voelt nu ouderwets, dat weet ik best. Maar we zijn volgens mij iets kwijtgeraakt toen we ermee ophielden, en wat dat kost begint zichtbaar te worden op de elektriciteitsmeter.

Waar ik bytes leerde tellen

In de scene waren de beperkingen het hele punt. Een 64KB-intro moest z’n eigen graphics, muziek en animatie meedragen, het meeste gegenereerd op het moment zelf omdat er geen ruimte was om ook maar iets op te slaan. Elke byte was een beslissing die iemand had genomen.

Groepen als Future Crew, Farbrausch en Conspiracy maakten van die grens kunst. .kkrieger is het voorbeeld dat ik er nog steeds bij haal. Een complete first-person shooter, met textures en levels en vijanden, in 96KB. Eén moderne webfont is groter dan dat.

Was het masochisme? Een beetje. Maar vooral was het vakmanschap. Je moest de hardware goed genoeg kennen om er prestaties uit te halen die volgens het spec sheet niet bestonden. Dat is precies het begrip dat ik nu nog het meest waardeer in m’n werk. Weet je waarom iets snel is, dan weet je ook waarom het stukgaat.

Hoe we ermee ophielden

Toen schoof de grond weg.

Cloud maakte van oneindige schaal een afrekenknop. Moore’s Law bleef uitkeren. Opslag ging van kostbaar naar gratis. En de gewoonte om te optimaliseren liep leeg, omdat de economie er niet meer voor betaalde.

Waarom drie dagen aan een algoritme sleutelen als een grotere instance minder kost dan je middag? Waarom assets comprimeren als bandbreedte in fracties van een cent wordt afgerekend? Waarom iets profilen als de klant gewoon meer RAM koopt? Op elk van die vragen bestaat een verdedigbaar antwoord. Ik heb het luie antwoord zelf vaak genoeg gegeven.

Zo ziet dat eruit in de enterprise-omgevingen waar ik heb gewerkt:

  • Applicaties die 32GB RAM nodig hebben om een spreadsheet te tonen
  • Docker-images van gigabytes voor services die bijna niks doen
  • JavaScript-bundles die langer duren om te parsen dan de interactie die ze mogelijk maken
  • “Microservices” die meer resources vragen dan de monoliet die ze vervingen

Ergens hebben we besloten, zonder er ooit over te stemmen, dat ontwikkelaarstijd duur is en compute goedkoop. Dus geven we compute uit om tijd te sparen. Dat was een prima ruil zolang er alleen een cloudrekening op het spel stond. Of dat nog zo is, weet ik niet meer zo zeker.

De rekening die niet op de mat valt

Elke CPU-cyclus die je niet nodig hebt verbrandt evengoed stroom. Een opgeblazen image heeft evengoed een schijf nodig om op te staan en een netwerk om overheen te gaan. Een over-provisioned Kubernetes-cluster trekt de klok rond stroom om idle cores warm te houden.

Datacenters gebruiken al ergens tussen de 1 en 2 procent van de elektriciteit in de wereld, en dat getal klimt hard nu de AI-workloads erbovenop zijn gekomen. Draait je inefficiënte code op één laptop, dan merkt niemand het. Draait hij op tienduizend instances, dan draait de verspilling ook op tienduizend instances. Op die schaal is het verschil tussen O(n) en O(n²) geen whiteboard-oefening meer. Het wordt kilowatturen, en verderop in de keten opwekcapaciteit en koolstof.

Dit is het stuk waar ik steeds op terugkom. We hebben compute als wegwerp behandeld. Pak wat je nodig hebt, schaal on-demand, kijk nooit naar de verspilling. Dat ging goed zolang de tweede rekening uitbleef, en die begint nu binnen te komen.

Wat een demoscener met een cluster zou doen

Het meeste van die oude mentaliteit overleeft de sprong van 64KB-intro’s naar productiesystemen. Ik heb er vier gewoontes aan overgehouden.

Meet eerst. Demosceners gokten nooit wat traag was. Ze profileerden tot ze het wisten. Wij hebben observability-tooling waar zij alleen van konden dromen, en we richten die vooral op debuggen in plaats van op efficiëntie. De data ligt er al.

Bevraag de default. Heeft deze service 2GB basisgeheugen nodig, of kwam hij met dat getal en heeft niemand het ooit nagekeken? Heeft deze JSON-response elk veld nodig? Moet deze cronjob elke minuut draaien? Meestal is het antwoord “geen idee, dat is altijd zo geweest”.

Behandel de beperking als een feature. Geef jezelf een imagelimiet van 50MB. Dwing een service in 256MB te draaien. Ik heb zulke limieten schonere ontwerpen zien opleveren, net zoals 64KB de scene dwong procedurele generatie uit te vinden omdat niks anders paste.

Maak efficiëntie zichtbaar. In de scene leverde klein en snel respect op. In de meeste bedrijven is het onzichtbaar werk dat niemand op de sprintreview demonstreert. We vieren de feature. We zouden net zo goed de engineer kunnen vieren die de clusterfootprint halveerde.

Waar je deze week mee kunt beginnen

Ik stel niet voor dat je een demo gaat schrijven. Maar een aantal gewoontes uit die wereld past één op één op hoe we infrastructuur draaien, en geen ervan heeft een budget nodig.

Profile voordat je schaalt. Zoek eerst uit waarom de huidige instance het zwaar heeft, voordat je naar een grotere grijpt. In mijn ervaring is het één slechte databasequery of een trage memory leak, en die fix is goedkoper dan de upgrade.

Right-size alles. Kubernetes resource requests en limits bepalen hoeveel cores er idle en warm staan. Zet ze op wat de service meet, want de waarden waarmee hij geleverd werd waren een gok.

Meet energie direct. Scaphandre, Kepler en Cloud Carbon Footprint laten het werkelijke energieverbruik van je workloads zien. De meeste teams hebben er nog nooit één keer naar gekeken.

Schedule met het net in gedachten. Draai batchjobs buiten de piek, of wanneer het net groener is. Sommige providers bieden carbon-aware scheduling inmiddels standaard aan, en het aanzetten kost niks.

Cache agressief. Een berekening die je nooit draait trekt nul watt. Caching betaalt je terug in latency en in stroom tegelijk.

Kies de taal bij het werk. Rust en Go verbranden veel minder energie dan Python of Ruby voor hetzelfde werk. Voor een script dat één keer per dag draait maakt dat niks uit. Op schaal wel.

Waarom het nog niet gebeurd is

Ik weet waarom ik dit zelf niet op elke service heb gedaan die ik draai. De prikkels wijzen de andere kant op. Optimaliseren is echt engineeringwerk zonder demo aan het eind, en de meeste roadmaps belonen de nieuwe feature boven de slankere. Een cloudrekening is een getal dat een CFO snapt. Een koolstofvoetafdruk voorlopig meestal niet.

Ik heb het opgeblazen ding ook op een deadline geshipt. Niks hiervan is een morele misstap. Het is een default waar we in zijn afgedwaald omdat elke losse shortcut op dat moment verstandig was.

Eén service

Moet ik aanwijzen waar je begint, dan bij één service die niemand mist als hij even wankelt. Profile hem, zet z’n requests op wat hij meet, hang er een energiemeter naast en kijk wat het getal doet.

De demoscene liet zien dat een harde grens je vindingrijk maakt. Dat gold toen de grens een bestand van 64KB was. Het geldt opnieuw nu de grens het stroomnet is, en ik leer het liever opnieuw van de scene dan van de meter.


De demoscene is inmiddels UNESCO-erkend cultureel erfgoed in verschillende landen.