Skip to content

defk-o08.3: the copy analysis asks each arm's reads in one pass - #69

Merged
DanielLuning merged 2 commits into
mainfrom
defk-o08.3/copies-arm-reads
Oct 20, 2026
Merged

DanielLuning merged 2 commits into
mainfrom
defk-o08.3/copies-arm-reads

Conversation

@DanielLuning

Copy link
Copy Markdown
Owner

Merge #68 first. This branch builds on defk-o08.2/compile-time (the timing harness it was measured with). Until #68 merges, this PR shows that commit too.

What changed

Copies.nested/3 asked three questions for every block nested in a statement: arm?/2, taken/2 and reads_outside/2. Together they made five walks of the whole statement, and taken/2 was quadratic, because it called holds_arm?/2 (a walk of the arms) on every node. This was the largest single cost in the compiler: about 10% of the time.

  • Copies.arm_reads/2 gives the same answer in three linear walks:
    1. One prewalk counts the occurrences and checks that the node is directly an arm.
    2. cut/2 rebuilds only the path to the arm. It mirrors Macro.prewalk's descent, and on the way back it cuts each if, cond and case as other_arms_out/2 does.
    3. reads/1 reads what is left.
  • The old functions stay as the spec. test/copies_test.exs has a property test that compares arm_reads/2 with arm? plus reads_outside(taken(...)) on every subterm of generated statements. The statements contain if, cond, case, matches and blocks, and they use few names, so that a node often appears twice.
  • Docs: docs/performance.md 6.39, and ADR-0007's cost note.

Results

  • The C: mix dump_c is byte-identical for all 108 modules. The only difference is the known path-hashed defk:loop key in Defk.ReportTest.Gen.c.
  • Compiler work: measured with defk-o08.2: compiler time measured warm in a fresh process; the chain measured again against its budget #68's harness, two interleaved rounds against main plus the harness:
    • reductions: 138.6 M → 125.8 M (−9.2%; −5.0% without SatEx.Kernels);
    • time: 1,101 → 996 ms (−9.6%; −2.9% without SatEx.Kernels);
    • SatEx.Kernels: −8.4 M reductions, −86 ms.
  • The budget: against the compiler before the symbolic-bounds chain, the work is now +17.6% (+8.4% without SatEx.Kernels), down from +29.5%. That is still above the 15% ceiling; two follow-up cuts are ticketed.

Provisional: the laptop was on battery, with no quiet-machine check.

How it was checked

  • mix ci on the laptop (macOS) passed: 2302 tests, Credo found no issues, Dialyzer reported 0 errors.

  • A mutation run: with the condition of an if kept instead of cut, the property test and the example test both failed. The line was then restored.

  • The byte-identical mix dump_c gate, against an untouched main checkout.

  • Touches a promised surface? No. No CHANGELOG.md entry.

  • Touches test/stability/? No.

… measured again against its budget

DEFK_COMPILE_TIME=<file> makes every module compile once more in a fresh
process and log its microseconds and reductions; dev/compile_time.exs
compares interleaved rounds of two checkouts. The old method timed a
first compile, which under mix dump_c waited ~1 s for the compiler's own
modules, and a second compile in the same process varied twofold. Measured
again: the symbolic-bounds chain +13.4% time / +20.8% reductions, the
refactors after it +13.5% / +7.3%, together ~29% past the compiler before
step 1 (docs/performance.md 6.38, ADR-0007's cost note).
Copies.nested/3 asked arm?/2, taken/2 and reads_outside/2 for every
nested block: five walks of the statement, taken/2 quadratic.
arm_reads/2 gives the same answer in three linear walks, rebuilding only
the path to the arm; the old functions stay as the spec, and a property
test compares the two on every subterm of generated statements. C
byte-identical; the compiler's work -9.2% (reductions), time -9.6%,
+17.6% of the work before the symbolic-bounds chain (docs/performance.md
6.39).
@DanielLuning
DanielLuning merged commit 9a3055b into main Oct 20, 2026
2 checks passed
@DanielLuning
DanielLuning deleted the defk-o08.3/copies-arm-reads branch October 20, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant