Přeskočit na obsah

Tato stránka byla automaticky přeložena z angličtiny, jediného jazyka, ve kterém se tento web píše. Číst originál.

Pravda o výkonu

kam jde čas příkazu (`MJ_TIMING=1`), co bylo pomalé a podoba každé opravy, `majordomus bench` a jeho studená i teplá rozdělení, lokální běhy proti sledované základní hodnotě, kontrola regrese a rozpočty a jak na výkonu pracovat

Vykresleno z docs/PERFORMANCE.md — stejného Markdownu, který ukazuje GitHub.

Jak toto úložiště ví, co je pomalé, proč a zda to změna zrychlila: hlášení fází u každého příkazu, jeden harness měření výkonu odvozený z registru příkazů, lokální důkazy, které nikdy nejsou základním stavem, sledovaný základní stav, který se nikdy needituje ručně, a kontrola, která regresi odmítne politikou. Pravidla za tím jsou project.derived-once, project.benchmarkable-commands a project.performance-evidence pod .ai/repo/rules/project/.

Kam jde čas: MJ_TIMING

Nastavte MJ_TIMING=1 u libovolného příkazu a jeho fáze a čítače práce se při ukončení vypíšou na stderr, seřazené podle času; stdout zůstává nedotčen.

$ MJ_TIMING=1 majordomus doctor >/dev/null
TIMING clock=epochrealtime total=2365 ms
phase       648 ms     1 x  validate:context
phase       446 ms     1 x  ctxd:scan
phase       341 ms     1 x  validate:catalogue
...
count       108     yaml_flatten
count        22     git

Fáze se deklaruje tam, kde příkaz dělá jednu věc: dispatcher doktrín deklaruje jednu na validátor, kompilátor znalostí jednu na fázi, generátor webu jednu na sekci. Čítač je jedna jednotka práce, která stojí za počítání: zploštění YAML, zploštění dávky, hash dávky, volání gitu. Čítače jsou strukturální důkaz: smyčka, která zregredovala na jeden proces na položku, se projeví jako počet rostoucí se vstupem, dlouho předtím, než si hodin všimnou na malém repozitáři. Hodiny jsou EPOCHREALTIME, kde je bash má, a jinak celé sekundy, a report říká, které.

Co bylo pomalé a tvar opravy

Každé takto nalezené horké místo mělo stejný tvar: práce závislá jen na nezměněných kanonických souborech, opakovaná pro každou položku, kde každá položka platí za proces. Proces stojí milisekundy; tisíc jich stojí nástroj jeho uživatele. Opravy mají také stejný tvar:

  • mnoho souborů jedním procesem: mj_yaml_flatten_many zploští libovolný počet souborů v jednom awk (front matter jen s --front, číslované výstupy, když se shodují basenames); mj_sha256_many hashuje seznam v jednom procesu; lib/plan_json.awk a lib/mermaid.awk sestaví JSON a grafy plánu jedním průchodem modelem;
  • soubor do proměnných jednou: mj_yload a mj_yload_dir převedou plochý soubor, nebo adresář takových souborů, na proměnné shellu jedním awk a jedním eval; mj_yv a mj_yvlist je čtou expanzí a tak je čtou přístupové funkce plánu, katalog, kontextové dokumenty i generátor webu;
  • nic na řádek, co by forkovalo: tabulátor pro IFS se spočítá jednou (MJ_TAB), podřetězec nahrazuje cut a řetězce a pole JSON generátoru sestavují jstr, jarr a jline místo jq na každou hodnotu.

Změřené rozdíly každého kroku jsou ve zprávách commitů (git log --grep perf), přijatý stav je baseline.json pod .ai/repo/benchmarks/, jakmile bude zapsán, a historie lokálních běhů je pod .ai/local/benchmarks/. Tento dokument záměrně nenese žádná čísla: zastarávají, a soubory výše ne.

Měření výkonu: majordomus bench

bench měří čas každého veřejného příkazu share/commands.yaml v jednorázovém repozitáři. Nic nevede seznam: příkaz přidaný do registru je cílem od té chvíle a harness nikdy neměří sám sebe. Pokud je přítomna vlastní sada testů nástroje, scénářem cíle je první scénář jeho fixture pod test/fixtures/commands/, takže se měří to, co web předvádí a případ 34 provádí; jinak je to holý příkaz v nainstalovaném repozitáři.

Studené a teplé se zaznamenávají odděleně a nikdy se neprůměrují dohromady. Příkaz jen pro čtení se spustí jednou studeně, benchmark.warmupkrát se zahřeje, poté se benchmark.sampleskrát vzorkuje ve stejném repozitáři. Příkaz, který mění stav, dostane na každý vzorek nový repozitář: každý jeho vzorek je studený a nemá teplé rozdělení. Vzorek je pouze wall-clock čas podřízeného procesu; příprava nikdy není uvnitř hodin. Každé rozdělení nese svůj počet, minimum, p50, p90, p95, p99, maximum, průměr a směrodatnou odchylku.

majordomus bench                       # every target
majordomus bench doctor context        # these two
majordomus bench --list                # the targets, their class and scenario
majordomus bench --format json         # the run as one document
majordomus bench --samples 30 --warmup 5

Reference příkazů je sekce bench dokumentu CLI.md; tvary dokumentů jsou v SCHEMAS.md.

Důkazy, baseline, kontrola

Tři věci, které se snadno pletou a jsou drženy odděleně:

  • Běh je lokální důkaz: .ai/local/benchmarks/runs/<run-id>.json, latest.json, jeden řádek na běh v history.jsonl. Zaznamenává commit, zda byl strom špinavý, platformu a profil. Git jej ignoruje a nic nedokazuje o jiném stroji.
  • Základna je přijatý stav: baseline.json pod .ai/repo/benchmarks/, sledovaný, zapisovaný jen příkazem majordomus bench --write-baseline na čistém stromě (nebo s --force), ve vlastním commitu s důvodem. Revize probíhá jako u každé jiné změny.
  • Kontrola je majordomus bench --check: čerstvý běh porovnaný se základní linií, po cílech a režimech, na p50, p95 a p99; hodnota o víc než zlomek benchmark.regression politiky nad základní linií je FAIL jmenující metriku, obě hodnoty a práh a příkaz skončí kódem 10. Bez základní linie skončí 12; základní linie s jiným schématem není srovnatelná a skončí 15 místo vytvoření čísla.
  • Regrese potřebuje dva vzorky, které se shodují. Každý vzorek je držen na stejné mezi, baseline × (1 + práh), a počítán: metrika nad prahem v jednom vzorku (jediný studený vzorek read-only příkazu, nebo jediný pomalý vzorek, na kterém spočívá malé p95) je WARN k přeměření a nezpůsobí selhání. --cold-samples <n> bere další studené vzorky read-only příkazu, každý jako první běh v repozitáři, který mu patří. Každé zjištění jmenuje vzorky, na kterých spočívá (on 2 of 3 sample(s)), runner a jeho 1minutový load average na začátku a na konci běhu: dne 2026-10-08 jediný studený vzorek version četl 9 -> 23 ms na přetíženém runneru a shodil změnu, která neprováděla žádný měřený kód. bench --check spustitelného souboru Rustu drží svou baseline na stejném pravidle (WARN, agree/n) a jmenuje svůj runner a zátěž na řádku verdiktu.

Prahy podle nástěnných hodin jsou mezi stroji hlučné, proto jsou zlomky v politice konzervativní a relativní; přesným důkazem jsou čítače.

Rozpočty

Politika deklaruje rozpočet pro příkazy, za které hooky platí: benchmark.budget.doctor_ms a benchmark.budget.watch_ms. Každý příkaz na konci svého běhu hlásí svůj vlastní wall time proti rozpočtu, INFO pod a WARN nad, a nikdy nenechá, aby to změnilo exit kód. Rozpočet se stanoví po běhu, ne před ním, a běh se zapíše do politiky vedle čísla: kolik vzorků, na jakém stroji a v jakém stavu a medián, p90 a max, které vyprodukovaly. Rozpočet, jehož zdůvodnění není napsáno vedle, je další číslo, které nikdo neobhájí.

Tři pravidla dělají z jednoho z nich odpověditelnou věc, a ne jen přítomnou.

Z p90 s rezervou, nikdy z mediánu. Rozpočet splněný v polovině případů je varování, které se spouští v polovině případů, a varování, které je obvykle šum, nikdo nečte. Kolik rezervy, je samo měření: rozptyl mezi běhy doctor na jednom vývojářském stroji při pevném korpusu je blízko 4x, protože reálný čas tam je většinou souběžností hostitele. Proto rozpočet, který tyto klíče nesou, odmítá hrubou strukturální změnu — sestavení indexu na cestě doctoru, validátor, který prochází celý strom několikrát — a nic jemnějšího. Nástrojem pro cokoli jemnějšího je majordomus bench, který bere rozdělení místo jednoho vzorku a porovnává je se základem pod zlomky regression.

Celý příkaz, ne fáze. MJ_TIMING=1 jmenuje asi čtyřicet fází uvnitř doctor, ale na příkaz je jeden klíč rozpočtu, protože je jeden jeho čtenář (mj_report_budget v lib/doctor.sh); nic nevypisuje buňku rozpočtu u fáze, takže klíč na fázi by byl číslem bez čtenáře. Fáze slouží kontrole koherence, kterou rozpočet celého příkazu musí přežít: souhrn pod součtem částí, které obsahuje, je špatně podle důkazů samotného souboru, dřív než cokoli kdo změří. 3000 ms, které tento pár nesl od 2026-09-05 do 2026-09-12, přesně tímto testem propadlo — čtyři nejdražší fáze samy stály čtyřnásobek.

Dva subjekty, měřené odděleně. .ai/repo/policy.yaml posuzuje tyto příkazy nad korpusem tohoto repozitáře; share/skeleton/policy.yaml je posuzuje nad repozitářem v den, kdy přijme Majordomus. majordomus bench doctor měří druhé, protože spouští svůj cíl v jednorázovém repozitáři, a je to ono, co hýbe dvojicí kostry.

Politika

benchmark:
  samples: 10            # warm samples per target
  warmup: 2              # unsampled runs before them
  regression:            # fractions over the baseline that --check refuses
    p50: 0.5
    p95: 0.5
    p99: 0.6
  budget:                # reported by doctor and watch, WARN never exit
    doctor_ms: 100000    # measured; the policy records the twenty runs behind each
    watch_ms: 60000

Každý klíč se čte pomocí mj_pol_req: deklarovaný, nebo odmítnutý, žádná výchozí hodnota na straně čtenáře.

Práce na výkonu

  1. Nejdřív změřte: MJ_TIMING=1 bin/majordomus <command> a přečtěte si pořadí.
  2. Změňte nejmenší věc, která odstraní opakovanou práci; dávejte přednost jednomu procesu pro mnoho položek před cache, protože cache je druhá věc, které se dá věřit.
  3. Prokázat výstup beze změny: případ, který pokrývá cestu kódem, a u generátoru bajtové porovnání jeho výstupu před a po.
  4. Změřte znovu a obě čísla uveďte v commit message.
  5. Spusťte majordomus bench <command> a při přijetí změny aktualizujte baseline ve vlastním commitu.

Co se záměrně nedělá: žádná cache výsledků mezi procesy (dávková čtení ji učinila zbytečnou; pokud bude někdy přidána, nesmí změnit žádný pozorovatelný výsledek a musí být prokázána případem nad každým příkazem jen pro čtení), žádné hot reload, žádná metrická služba, žádný dashboard.

Prohledává názvy a popisy všech stránek, které tento web publikuje.