Demystifikace BEAM binárních dat: Jak jsme udělali naši DNS knihovnu bleskově rychlou (bez použití NIF)

Demystifikace BEAM binárních dat: Jak jsme udělali naši DNS knihovnu bleskově rychlou (bez použití NIF)

Říj 20, 2026 beam elixir erlang dns performance optimization binary protocols backend development

Když provozujete platformu, která řeší registraci domén, DNS hosting a cloudovou infrastrukturu jako my v NameOcean, výkon není jen „příjemným bonusem“ – je to základní stavební kámen spolehlivosti. Proto nás nadcházejí chytré příběhy o optimalizaci z ekosystému Erlang a proto vám chceme rozebrat jeden z našich nejoblíbenějších.

Nedávno jsme narazili na fascinující hloubkovou analýzu od týmu DNSimple o jejich práci na optimalizaci dns_erlang, jejich open-source knihovny pro DNS. Jejich výsledky byly ohromující: o 12 % méně CPU na každý dotaz a o 14 % více obsloužených dotazů za sekundu – a to vše díky čistým optimalizacím BEAM, bez nutnosti použití NIF. Pro každého, kdo staví vysoce propustné systémy v Elixiru nebo Erlangu, je to povinné četivo.

Pojďme si rozebrat, co je k tomu možné.

Mentální model binárních dat v BEAM

Tady je něco, co překvapí mnoho vývojářů Elixiru: připojování (appending) k binárním datům v těsné smyčce není tak náročné, jak byste si mohli myslet.

Když napíšete něco jako:

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

Můžete očekávat složitost O(n²) – každá iterace vytvoří nový binární blok a vše zkopíruje. Ale BEAM je chytřejší.

Protože jsou binární bloky nemutabilní (neměnné), kompilátor dokáže dokázat, že pokud byl acc právě vytvořen předchozím appendem a nikde jinde není uložena na něj reference, je bezpečné zapisovat přímo do této existující paměti. První append alokuje určitou rezervní kapacitu a následné appendy do ní zapisují. Výsledek? Amortizovaná složitost O(1) na jeden append – identicky jako u std::vector v C++.

Háček? Pokud váš binární blok „unikne“ (escapes) – což znamená, že je uložen do slovníku procesu, vrácen volajícímu nebo k něčemu připojen – zábava končí. Zase se vracíte ke kopírování.

Tento vzorec se objevuje všude při zpracování binárních protokolů. Když iterativně budujete odpovědi DNS, je nesmírně důležité udržet tento tok akumulátoru v těsné struktuře.

Sub-bináry: Bezplatné ořezávání (skoro)

Porovnávání (matching) v binárních blocích vytváří to, čemu se v BEAM říká sub-bináry – drobná data, která ukazují na původní data bez jejich kopírování. Když napíšete:

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

Žádná data se nekopírují. Rest je jen „okno“ do původního paketu.

Zde je ale jemnost, na kterou se lidé chytají: BEAM si interně udržuje kontext porovnávání, když provádíte rekurzivní matching – v podstatě kurzor procházející bajty. Sub-binár se nezkrystalizuje, dokud ho skutečně nenecháte „uniknout“ – tedy když ho nevrátíte, neuložíte nebo nepředáte někam, kde zůstane.

To je pro parsování DNS klíčové. Každý dotaz přichází jako binární blok, je porovnán se strukturou protokolu a musí vygenerovat odpověď. Pochopení toho, kdy vytváříte zbytečnou práci oproti pouhému pohledu na data, je rozdílem mezi parserem, který škáluje, a parserem, který pod zátěží selže.

Proč na reprezentaci celých čísel záleží více, než si myslíte

Na 64bitovém BEAM zabírají celá čísla několik bitů pro svou typovou značku. To zanechává 60 bitů pro samotnou hodnotu. Celá čísla, která se do tohoto prostoru vejdou, jsou „malá celá čísla“ (small integers) – okamžité hodnoty, které žijí přímo v registrech procesu a nikdy nedotknou haly (heap).

Všechno větší se stane „bignumem“, což vyžaduje alokaci na halě a zohlednění sběru odpadu (garbage collection). Zní to esotericky, ale stává se to kritickým, když provádíte typ „bitového kroucení“, které práce s binárními protokoly vyžaduje.

Tato technika se nazývá SWAR (SIMD Within A Register) – balení více hodnot do jednoho celého čísla, abyste na ně mohli operovat paralelně na úrovni CPU. Pro DNS to znamená parsování mnoha bajtů dat v jedné operaci. Ale funguje to jen tehdy, pokud vaše zabalené hodnoty zapadnou do ideálního rozsahu „malých celých čísel“.

Pokud se tak nestane, neplatíte jen náklady na alokaci paměti – potenciálně spouštíte cykly sběru odpadu, které by jinak neexistovaly. Při milionech dotazů za sekundu se tyto cykly sčítají.

Co to znamená pro váš kód

Zde je praktický závěr: nemusíte psát NIF nebo sáhnut po C, abyste dosáhli vážných výkonových skoků. Musíte pochopit, jak BEAM přemýšlí o datech.

Několik vzorců, které si je stojí osvojit:

1. Udržujte binární akumulátory v těsné struktuře. Smyčky typu „pouze připojování“ (append-only) jsou vaším přítelem. Jakmile binární blok unikne, model nákladů se dramaticky mění.

2. Porovnávejte přímo v místě, kde to jde. Používejte pattern matching k pohledu na data, nikoliv k jejich kopírování. BEAM je na toto optimalizováno.

3. Sledujte velikost svých celých čísel. Při provádění aritmetiky nebo bitové manipulace přemýšlejte o tom, zda vaše hodnoty zůstanou v rozsahu „malých celých čísel“.

4. Profilujte s ohledem na redukce. BEAM plánuje práci v redukcích (přibližně v počtu volání funkcí). „Horká“ smyčka, která provede více porovnávání v menším počtu volání funkcí, vykoná z pohledu plánovače méně práce.

Pro každého, kdo staví systémy pracující s binárními protokoly – ať už jde o DNS, WebSocket rámce nebo vlastní implementace protokolů – tyto vzorce platí přímo. BEAM vám poskytuje neuvěřitelné nástroje pro psaní rychlého kódu, ale jsou užitečné pouze tehdy, pokud rozumíte tomuto modelu.

Viděli jsme týmy, které sáhly po NIF nebo Rust NIF, přičemž hlubší pochopení BEAM semantiky by jim přineslo 90 % zíšků bez veškeré té složitosti. Runtime je chytřejší, než si často myslíme – naším úkolem je psát kód, který mu dovolí se předvést.

V NameOcean neustále přemýšlíme o výkonu při velkém měřítku. Ať už spouštíte Vibe Hosting pro svůj další projekt nebo spravujete DNS pro vysoce zatěžovanou aplikaci, pochopení těchto fondamentů vám pomůže stavět systémy, které zůstávají rychlé i v době největšího vytížení.

Šťastné kódování a ať se vaše binární bloky vždy připojují efektivně!

Read in other languages:

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