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_manyzploští libovolný počet souborů v jednom awk (front matter jen s--front, číslované výstupy, když se shodují basenames);mj_sha256_manyhashuje seznam v jednom procesu;lib/plan_json.awkalib/mermaid.awksestaví JSON a grafy plánu jedním průchodem modelem; - soubor do proměnných jednou:
mj_yloadamj_yload_dirpřevedou plochý soubor, nebo adresář takových souborů, na proměnné shellu jedním awk a jedním eval;mj_yvamj_yvlistje č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
IFSse spočítá jednou (MJ_TAB), podřetězec nahrazujecuta řetězce a pole JSON generátoru sestavujíjstr,jarrajlinemí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 vhistory.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.jsonpod.ai/repo/benchmarks/, sledovaný, zapisovaný jen příkazemmajordomus bench --write-baselinena č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ž zlomekbenchmark.regressionpolitiky nad základní linií jeFAILjmenující metriku, obě hodnoty a práh a příkaz skončí kódem10. Bez základní linie skončí12; základní linie s jiným schématem není srovnatelná a skončí15mí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
WARNk 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ý vzorekversiončetl 9 -> 23 ms na přetíženém runneru a shodil změnu, která neprováděla žádný měřený kód.bench --checkspustitelné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
- Nejdřív změřte:
MJ_TIMING=1 bin/majordomus <command>a přečtěte si pořadí. - 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.
- 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.
- Změřte znovu a obě čísla uveďte v commit message.
- 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.