Het ontrafelen van BEAM-binaries: Hoe we onze DNS-bibliotheek razendsnel maakten (zonder enige NIF)

Het ontrafelen van BEAM-binaries: Hoe we onze DNS-bibliotheek razendsnel maakten (zonder enige NIF)

Okt 20, 2026 beam elixir erlang dns performance optimization binary protocols backend development

Wanneer je een platform beheert dat domeinregistratie, DNS-hosting en cloud-infrastructuur afhandelt zoals wij bij NameOcean, dan is prestatie niet zomaar een extraatje—het is de basis van betrouwbaarheid. Daarom worden we enthousiast wanneer we slimme optimalisatieverhalen uit het Erlang-ecosysteem zien, en daarom willen we er een van onze favorieten voor je ontleden.

Onlangs kwamen we een fascinerende diepgaande analyse tegen van het DNSimple-team over hun werk aan het optimaliseren van dns_erlang, hun open-source DNS-bibliotheek. Hun resultaten waren opmerkelijk: 12% minder CPU per query en 14% meer queries per seconde—en dat alles door pure BEAM-optimalisaties, zonder dat er NIF's nodig waren. Voor iedereen die systemen met een hoge doorvoer bouwt in Elixir of Erlang, is dit verplichte kost.

Laten we ontleden wat dit mogelijk maakt.

Het BEAM Binary Mentale Model

Hier is iets dat veel Elixir-ontwikkelaars verrast: het toevoegen (appending) aan binaries in een strakke loop is niet zo duur als je zou denken.

Wanneer je iets schrijft als:

def build_response(acc, []) do
  acc
end
def build_response(acc, [h | t]) do
  build_response(<<acc::binary, h>>, t)
end

Zou je verwachten dat dit O(n²) is—waarbij elke iteratie een nieuwe binary maakt en alles kopieert. Maar de BEAM is slimmer dan dat.

Omdat binaries onveranderlijk (immutable) zijn, kan de compiler bewijzen dat als acc net is aangemaakt door een eerdere append en er verder geen enkele referentie naar bestaat, het direct schrijven in die bestaande geheugenruimte veilig is. De eerste append reserveert wat extra capaciteit, en opeenvolgende appends schrijven daar simpelweg in. Het resultaat? Amortized O(1) per append—identiek aan hoe een std::vector in C++ werkt.

De crux? Als je binary "ontsnapt" (escapes)—wat betekent dat hij wordt opgeslagen in een process dictionary, wordt geretourneerd naar een aanroeper, of wordt toegevoegd aan iets anders—dan is het feest voorbij. Je bent dan terug bij het kopiëren.

Dit patroon komt overal voor bij het verwerken van binaire protocollen. Wanneer je DNS-responses iteratief opbouwt, is het essentieel om die accumulator-flow strak te houden.

Sub-binaries: Gratis Slicing (Grotendeels)

Het matchen op binaries creëert wat de BEAM "sub-binaries" noemt—kleine termen die naar de originele data wijzen zonder deze te kopiëren. Wanneer je schrijft:

<<Header:12/binary, Rest/binary>> = Packet

Wordt er geen data gekopieerd. Rest is slechts een venster naar het originele pakket.

Maar hier zit de subtiliteit die mensen in de val laat lopen: de BEAM houdt intern een match-context bij wanneer je recursief matcht, in feite een cursor die door de bytes loopt. De BEAM zal een sub-binary pas echt materialiseren als je hem echt laat ontsnappen—door hem te retourneren, op te slaan, of ergens door te geven waar hij blijft bestaan.

Dit is enorm belangrijk voor DNS-parsing. Elke query komt binnen als een binary, wordt gematcht tegen de protocolstructuur, en moet een response genereren. Begrijpen wanneer je werk creëert versus wanneer je alleen data bekijkt, is het verschil tussen een parser die schaalt en een parser die bezwijkt onder de belasting.

Waarom Integer-representatie belangrijker is dan je denkt

Op een 64-bit BEAM nemen integers een paar bits in beslag voor hun type-tag. Dat laat 60 bits over voor de werkelijke waarde. Integers die in deze ruimte passen zijn "small integers"—onmiddellijke waarden die direct in de process-registers leven en het heap (geheugen) nooit raken.

Alles wat groter is, wordt een bignum, wat heap-allocatie en overweging voor garbage collection vereist. Dit klinkt esoterisch, maar het wordt cruciaal wanneer je de soort bit-manipulatie doet die binaire protocol-arbeid vereist.

De techniek heet SWAR (SIMD Within A Register)—het verpakken van meerdere waarden in één enkele integer, zodat je ze op CPU-niveau parallel kunt verwerken. Voor DNS betekent dit het parsen van veel bytes aan data in elke operatie. Maar het werkt alleen als je verpakte waarden in de "sweet spot" van de small integers passen.

Wanneer ze dat niet doen, betaal je niet alleen de kosten van geheugenallocatie—je triggert potentieel garbage collection-cycli die anders niet zouden bestaan. Bij miljoenen queries per seconde tellen die cycli snel op.

Wat dit betekent voor jouw code

Hier is de praktische les: je hoeft geen NIF's te schrijven of naar C te grijpen voor serieuze prestatiewinsten. Je moet begrijpen hoe de BEAM over data denkt.

Enkele patronen die het waard zijn om te internaliseren:

1. Houd binary accumulators strak. Append-only loops zijn je vriend. Zodra een binary ontsnapt, verandert het kostenmodel drastisch.

2. Match in de plaats waar mogelijk. Gebruik pattern matching om data te bekijken in plaats van deze te kopiëren. De BEAM is hierop geoptimaliseerd.

3. Let op je integer-groottes. Wanneer je rekenkunde of bit-manipulatie uitvoert, denk er dan aan of je waarden binnen het bereik van "small integers" blijven.

4. Profileer met het oog op reductions. De BEAM plant werk in "reductions" (grofweg functie-aanroepen). Een hot loop die meer matching doet in minder functie-aanroepen, verricht minder werk vanuit het perspectief van de scheduler.

Voor iedereen die systemen bouwt die binaire protocollen verwerken—of dat nu DNS, WebSocket-frames of aangepaste protocol-implementaties zijn—zijn deze patronen direct van toepassing. De BEAM geeft je ongelooflijke tools om snelle code te schrijven, maar ze zijn alleen nuttig als je het model begrijpt.

We hebben teams gezien die naar NIF's of Rust NIF's grepen, terwijl een dieper begrip van de BEAM-semantiek hen 90% van de winst had opgeleverd zonder de complexiteit. De runtime is slimmer dan we vaak denken—onze taak is om code te schrijven waarmee de runtime kan schitteren.

Bij NameOcean denken we altijd aan prestaties op schaal. Of je nu Vibe Hosting opstart voor je volgende project of DNS beheert voor een applicatie met veel verkeer, het begrijpen van deze fundamenten helpt je om systemen te bouwen die snel blijven wanneer het druk wordt.

Happy coding, en moge je binaries altijd efficiënt blijven appenden.

Read in other languages:

PL IT BG RU CS EL UZ TR SV FI RO PT NB HU FR DE ES DA ZH-HANS EN