# Repeatable timing benchmark for the wt helpers in ../modules/git.nu. # # Methodology: # - fixture: throwaway bare+worktree layout built with raw git only # (5 worktrees, one dirty, 44 refs) under %TEMP%/wt-bench2 # - sides: old = snapshot of modules/git.nu at %TEMP%/wt-bench2/oldmod.nu # (create before first run: # mkdir C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod; cp nushell/modules/git.nu C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod/oldmod.nu ) # new = live modules/git.nu. Snapshot == live means the run is an A/A # calibration and must show old ~= new within noise. Delete the snapshot # to pin a new baseline after optimizing. Kept outside the fixture dir: # the fixture is wiped on every run. # - per scenario: 1 warmup per side, then 10 interleaved run pairs # (old, new) to cancel machine drift; wall time via timeit # - git spawn count via GIT_TRACE pointing at a per-run truncated file # - stats: median + MAD (robust for skewed wall times), min/max. # Decision rule: improvement iff new_med < old_med - old_mad and # old_med/new_med >= 1.2; regression iff new_med > old_med + old_mad. # - module import cost: interleaved `nu -c 'use ...git.nu'` vs bare # `nu -c ''`, 5 pairs, median difference. # # Run: nu nushell/tests/wt-bench.nu const repo = "C:/Users/okr/AppData/Roaming/nushell" const tmp = "C:/Users/okr/AppData/Local/Temp/wt-bench2" const root = "C:/Users/okr/AppData/Local/Temp/wt-bench2/layout" const gd = "C:/Users/okr/AppData/Local/Temp/wt-bench2/layout/.git" const trace = "C:/Users/okr/AppData/Local/Temp/wt-bench2/trace.log" use C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod/oldmod.nu use C:/Users/okr/AppData/Roaming/nushell/modules/git.nu * def quiet-git [args: list] { (^git ...$args | complete) | ignore } def build-fixture [] { rm -rf $tmp mkdir $tmp let seed = ($tmp | path join "seedrepo") ^git init --quiet --initial-branch main $seed ^git -C $seed -c user.email=bench@local -c user.name=bench commit --allow-empty --quiet -m seed ^git init --quiet --bare $gd ^git --git-dir $gd fetch --quiet ($seed | path join ".git") "refs/heads/main:refs/heads/main" ^git --git-dir $gd symbolic-ref HEAD refs/heads/main quiet-git ["--git-dir" $gd "worktree" "add" ($root | path join "main") "main"] for i in 1..4 { ^git --git-dir $gd branch $"b($i)" main quiet-git ["--git-dir" $gd "worktree" "add" ($root | path join $"b($i)") $"b($i)"] } for i in 5..40 { ^git --git-dir $gd branch $"b($i)" } ^git --git-dir $gd branch feature/one ^git --git-dir $gd branch feature/two "dirty" | save --force ($root | path join "b1" "dirty.txt") } def spawn-count [file: path] { if ($file | path exists) { open --raw $file | lines | where {|l| $l | str contains "trace: built-in" } | length } else { 0 } } def run-once [block: closure] { "" | save --force $trace let dur = (timeit { do $block }) {dur: $dur, spawns: (spawn-count $trace)} } def stats [durs: list] { let med = ($durs | math median) { med: $med mad: ($durs | each {|d| ($d - $med) | math abs } | math median) min: ($durs | math min) max: ($durs | math max) } } def fmt-ms [d] { ($d / 1ms) | math round --precision 1 } def summarize [scenario: string, side: string, runs: list] { let s = (stats ($runs | get dur)) { scenario: $scenario side: $side "med(ms)": (fmt-ms $s.med) "mad(ms)": (fmt-ms $s.mad) "min(ms)": (fmt-ms $s.min) "max(ms)": (fmt-ms $s.max) spawns: ($runs | get spawns | math median) } } let scenarios = [ {name: "wt ls", old: {|| oldmod wt ls | ignore }, new: {|| wt ls | ignore }} {name: "wt summary", old: {|| oldmod wt | ignore }, new: {|| wt | ignore }} {name: "wt path", old: {|| oldmod wt path b7 | ignore }, new: {|| wt path b7 | ignore }} {name: "wt root", old: {|| oldmod wt root | ignore }, new: {|| wt root | ignore }} {name: "wt add scan", old: {|| try { oldmod wt add zzzznope } }, new: {|| try { wt add zzzznope } }} ] build-fixture if not ("C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod/oldmod.nu" | path exists) { mkdir C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod cp $"($repo)/modules/git.nu" "C:/Users/okr/AppData/Local/Temp/wt-bench-oldmod/oldmod.nu" print "oldmod snapshot created (this run = A/A calibration)" } cd ($root | path join "main") $env.GIT_TRACE = $trace mut results = [] for s in $scenarios { run-once $s.old | ignore run-once $s.new | ignore for i in 1..10 { let o = (run-once $s.old) let n = (run-once $s.new) $results = ($results | append {scenario: $s.name, side: "old", dur: $o.dur, spawns: $o.spawns}) $results = ($results | append {scenario: $s.name, side: "new", dur: $n.dur, spawns: $n.spawns}) } } mut table = [] for s in $scenarios { let rows = ($results | where scenario == $s.name) $table = ($table | append (summarize $s.name "old" ($rows | where side == old))) $table = ($table | append (summarize $s.name "new" ($rows | where side == new))) } print ($table | table --index false) # Module import cost: parse of git.nu paid by every new nu process that uses it. mut imports = [] for i in 1..5 { let t_use = (timeit { ^nu --commands $"use ($repo)/modules/git.nu" | ignore }) let t_base = (timeit { ^nu --commands "" | ignore }) $imports = ($imports | append {use: $t_use, base: $t_base}) } let use_med = (fmt-ms ($imports | get use | math median)) let base_med = (fmt-ms ($imports | get base | math median)) let parse_cost = (($use_med - $base_med) | math round --precision 1) print $"module import: use=($use_med)ms base=($base_med)ms parse~($parse_cost)ms — median of 5 pairs"