Learning
Všechny články
Engineering5. října 2026 6 min

Závislý kód čeká na celé tělo, potřebuje jen rozhraní

Rust build zrychlil o polovinu, protože zjemnil jedinou hranu v grafu

Pomalý build často netvoří velké uzly, ale příliš hrubé hrany mezi nimi

Rozdělit projekt do mnoha crate je nutná podmínka paralelního buildu, ne postačující. Když každý crate čeká, až ten pod ním úplně dokončí kontrolu, workspace se i na šestnácti jádrech překládá jako schodiště, jeden stupeň po druhém.

Projekt headstart ukazuje, že ta čekací hrana je zbytečně hrubá. Závislý crate se nepřekládá proti tělům funkcí, ale proti rozhraní. Stačí ho pustit ve chvíli, kdy je rozhraní hotové, a těla se dokontrolují souběžně s tím, co je nad nimi.


Na co dependent opravdu čeká

Dnes rustc zapíše metadata (.rmeta) až po type-checku a borrow-checku všech těl. Dependent z nich přitom čte signatury, typy, implementace traitů a MIR jen pro to, co může vyhodnotit při překladu, inlinovat nebo instancovat. Většinu práce na tělech nikdy nepotřebuje.

Headstart rozdělí analýzu na dvě fáze. Po kontrole rozhraní zapíše .early-rmeta a cargo na tu notifikaci rovnou spustí dependenty. U cargo check doběhnou celé jen na early metadatech. U cargo build udělají celou analýzu a před generováním kódu počkají na plná metadata, protože instancování generik potřebuje MIR těl.

Na 13 reálných projektech to dalo až 54 % u check a až 42 % u build, žádný projekt nebyl pomalejší. Clean build codex-rs na 16 jádrech zrychlil o 37 %: schodiště workspace crate se změnilo v překryv. Zisk pochází z jader, která by build jinak nechal stát, takže na menším stroji klesá. Na 4 jádrech je check rust-analyzeru rychlejší o 24 %.

Tohle doplňuje obvyklou radu „rozděl kód do víc jednotek". Granularita jednotek určuje strop paralelismu. Granularita hran určuje, kolik z něj skutečně využiješ.


Rozhraní není to, co vypadá jako rozhraní

Zajímavá část je, že hranice mezi rozhraním a implementací neleží tam, kde ji vidí oko. Některá těla do rozhraní prosakují a musí se zkontrolovat dřív.

Skrytý typ za impl Trait v signatuře vzniká až type-checkem těla funkce. Konstanty a const fn se do metadat zapisují jako MIR nebo hodnota. U async fn dependent potřebuje znát stavový automat i s tím, co drží přes await, protože podle toho rozhoduje, jestli je future Send. Tahle „těla rozhraní" se kontrolují před zápisem early metadat, zbytek až po něm.

První verze to řešila jinak: definici zapsala, pokud už náhodou byla zkontrolovaná, což zjišťovala nahlédnutím do cache dotazů. Tím metadata začala záviset na pořadí vyhodnocení. Výsledná množina proto musí být funkcí kódu, ne průběhu kompilace, a přepínač -Zearly-metadata-verify hlídá, aby se mimo ni nic nekontrolovalo.


Spekulace je bezpečná, když zadržíš výstup

Dependent, který startuje na early metadatech, se překládá proti slibu. Pokud se v těle závislosti objeví chyba, jeho práce se zahodí. Aby to uživatel nepoznal, cargo drží jeho diagnostiku i JSON výstup, dokud všechny závislosti neskončí čistě, a pak je vydá v původním pořadí. Selhávající build hlásí stejné chyby a stejný exit kód jako dnes, liší se jen průběžné řádky.

Selhání se šíří přes zámky souborů. Crate drží exkluzivní zámek na .rmeta a .rlib, dokud je nezapíše. Čekající kompilace si bere sdílený zámek. Když producent spadne, operační systém zámek uvolní, soubor chybí a čekající ví, že má skončit. Úklid po pádu tak nikdo řešit nemusí.

Čekání je druhá past. Pozastavená kompilace vrací svůj slot v jobserveru, aby jádro nestálo. Verze, která po probuzení čekala na volný slot, hladověla, protože soupeřila s prací spuštěnou místo ní, často na kritické cestě. Výsledné řešení radši nakrátko překročí limit o jeden job.

Cenu to má. Chyby crate se ohlásí o kousek později, u mediánu o 4 ms, u p99 o 249 ms. Právě zpoždění chyb byl jeden ze dvou důvodů, proč pokus z roku 2019 skončil.


Co si z toho odnést

Když pipeline stojí, nedívej se jen na nejpomalejší uzel. Projdi hrany a u každé se zeptej, na co konzument doopravdy čeká. Často na celý výstup, i když potřebuje jen jeho tvar.

Rozhraní definuj podle toho, co konzument může pozorovat, ne podle syntaxe. A rozsah toho, co do rozhraní patří, musí jít odvodit z kódu, jinak build přestane být deterministický.

Spekulativní start je v pořádku, pokud výsledky zadržíš do potvrzení slibu. Pak uživatel vidí rychlejší build se stejným výstupem, ne nový druh chyb.

Zdroje

Poslech místo čtení — 2 zdroje k načtení.