Skip to content

Latest commit

 

History

History
743 lines (620 loc) · 45.7 KB

vself_speed.md

File metadata and controls

743 lines (620 loc) · 45.7 KB

V self-compilation speed

Every compiler fix must preserve or improve self-compilation efficiency. Agents must compare retired instructions before and after compiler changes and resolve regressions before merging. Keep correctness tests and generated-output checks alongside performance measurements.

Compact semantic type descriptors

types.Type is again 16 bytes on 64-bit targets, down from the tagged-union revision's 72 bytes. Its seven large variants hold references to immutable payloads; the remaining variants stay inline. Promoted fields, constructors, value equality, semantic hashing, and owned copies retain their semantics. Shared defaults avoid allocations for empty payloads. The layout and ownership regression tests are in vlib/v/types/type_layout_test.v and the interner tests. See vlib/v/types/README.md for the payload ownership rules.

The combined patch also reduces repeated type, smartcast, source-name, and C-name lookups, retains bounded transform-helper storage through C generation, and shortens builtin map, string-search, and small-copy paths. FastC, C, and SSA regression tests cover indirect payload defaults, promoted initializers, and callback aliases needed by the compact representation.

A fresh comparison against master 5f7d4d09805ebbef64650d7bf275a007c7120e12 used the same 559,990-line fixture at 2d1367e589ed30c03325e586a8ddfd3bc96c8c3d. Both roles used the same seed, two in-place TCC rebuilds, Apple Clang 21 production builds with -mcpu=apple-m1, and fresh matching PGO profiles trained on this fixture with default and single-worker modes. The compiler build directories differed; measured executable, input, output, and working-directory paths matched. The measured candidate combines compact types and hot-path changes in the retained compact-v76 source snapshot. Subsequent FastC correctness fixes and master integration, including atomic cache portability changes, are outside this measurement. The final reviewed revision has not been benchmarked.

One warmup per role preceded ten alternating measured pairs with VJOBS=1. All 22 runs succeeded, each with one positive retired-instruction counter and byte-identical generated C. The common output passed Apple Clang's Objective-C syntax check with the fixture's include paths.

Median metric Master Compact types and hot-path changes
Retired instructions 56,454,904,776.5 51,480,959,993.5
Instructions/parsed line 100,814.13 91,931.93
CPU cycles, billions 22.1275 21.1105
Peak resident memory, MiB 1,125.72 1,172.06
Elapsed seconds 7.3736 6.9148

The candidate used 8.81% fewer instructions, with 0/10 regressing pairs and a median paired difference of -4,934,100,840.5 instructions. Peak memory increased by about 46 MiB. Elapsed time and cycles are contextual shared-host measurements, not controlled latency claims. The 90,000 instructions/line target remains unmet: it requires 50,399,100,000 instructions on this unchanged fixture. Keep the fixture and denominator fixed when checking future changes.

The baseline executable SHA-256 is 56b5b8210947b185aa7e73585ba33f4df072dabd2d0e5403ed8cbcc7db0e82bd; the candidate executable SHA-256 is 15fc29aa638170d24cd98973f62d7bb5d48dcf638063695462b82a264680dcbe. Generated C has SHA-256 ade8fd366a802bee022b06cdb2d3e5167988f2a918f031f10eb6586f6e50660f.

Exact build commands, source snapshots, fixture inventories, binary hashes, all raw samples, and generated-output checks are retained in /Users/alex/code/v3-self-90k-artifacts/compact-type-20261021/. Use build.py, measure.py, baseline-master/metadata.json, compact-v76-standard/metadata.json, compact-v76-source/sha256.json, and compact-v76-pr-confirmation{,-metadata,-summary}.json with their per-run logs. The current fixture manifest and counting procedure are recorded below. Exploratory variants are excluded from this comparison and from the patch.

Why the reported scores differed

The earlier 115,328 -> 95,877 instructions/line comparison used compiler sources and input at 257b514a, Apple Clang 21, and a 560,382-line fixture. Its two roles were ordinary production and PGO production builds.

The later 124,706.52 score was an unprofiled baseline, not its optimized result. Homebrew Clang 23 wrote a profile that the selected Apple LLVM 21 merger could not read, so self-building fell back to an ordinary production build. Fixing that tool lookup gave 101,502.13 in the same comparison, on the newer 559,990-line fixture.

Those historical scores changed compiler revision, input, and C toolchain. They must not be presented as a continuous performance series. A fresh revision check, described below, kept input and Apple Clang 21 fixed: 97,584.84 -> 105,074.91, or 7.68% more instructions. That comparison confirms a revision-associated increase as well as the reporting error. The follow-up bisection below locates the main increase at the tagged-union compiler change.

Recent-commit bisection

The PGO failure was bisected over the eight first-parent commits from 257b514a30 to 2d1367e589. The first commit activating the failure is 2d1367e589 (#30962), which re-enabled automatic Clang PGO for production self-builds. Its parent does not attempt automatic production PGO; a passing predicate there means no failing attempt, not successful PGO.

The underlying lookup predates that change: find_llvm_profdata was introduced in a75cd076ed and searched PATH, then xcrun, without consulting the selected C compiler's installation. With explicit Homebrew Clang 23 and Apple LLVM 21 selected for merging, the producer writes raw profile format 11 while the merger expects format 10. The merge fails and self-building falls back to an unprofiled compiler. Apple Clang 21 with its matching merger succeeds.

The bisect predicate compiled each historical, unmodified vself.v using one stable compiler. A small V-driver stand-in built a real Clang 23 instrumented program, and the historical tool ran it and attempted the real profile merge. This tests tool selection and fallback control flow; it is not a full compiler self-build at each checkpoint. It reproduces the same format mismatch as the retained full-build latest2-before-build.log. The toolchain lookup fix 8a6e8a32dc completed PGO in the same probe, providing a positive control.

Reproduction scripts, the complete Git bisect log, commands, and six checkpoint/control results are in /Users/alex/code/v3-self-90k-artifacts/bisect-20261021/, including pgo-bisect.py, pgo-predicate.py, pgo-bisect.log, and pgo-summary.json.

The instruction-count bisect identifies da62dd4bd7 (#29578), "v3: represent sum types as tagged unions", with immediate parent 7940bb1a8e. This is a separate change from the PGO activation. Both sides completed PGO with Apple Clang 21 and its matching profile merger.

Each checkpoint was rebuilt twice in place with TCC using a seed compatible with its recursive type representation. The resulting compiler then built its own instrumented and optimized production versions with Apple Clang 21 and -cflags '-mcpu=apple-m1'. PGO training used the same unchanged 559,990-line fixture, once with default workers and once with VJOBS=1. This fresh recipe differs from earlier sessions; compare roles within this session rather than treating their absolute scores as a continuation of earlier tables.

Three exploratory samples per checkpoint located the boundary. A separate confirmation used one warmup per role and ten alternating measured pairs, with VJOBS=1 and the same executable, source, and output paths. The parent was built in a second checkout to overlap C compilation; its exact build path is recorded. Fixture training and benchmark runs were serialized.

Median metric Parent 7940bb1a8e Tagged unions da62dd4bd7
Retired instructions 54,270,831,065.5 59,248,702,390.5
Instructions/parsed line 96,913.93 105,803.14
CPU cycles, billions 19.4757 21.6487
Peak resident memory, MiB 1,133.09 1,117.53
Elapsed seconds 5.0598 6.1247

The tagged-union commit used 9.17% more instructions, increasing work in all ten pairs. The median paired increase was 4,788,159,580 instructions. All 22 runs succeeded; generated C was consistent within each role and passed Clang syntax checking. C intentionally differs between these revisions, which change code generation. Elapsed time and cycles reflect shared host load. These results attribute the main increase to the commit, not to one isolated hunk.

A separate exploratory control disabled PGO for both production compilers while retaining Apple Clang 21 and the other build flags. Instructions/line increased from 116,908.51 to 125,334.54 (7.21% more, all three pairs worse). Generated C matched the PGO output for each revision. This supports a compiler-work increase that persists without PGO; the primary attribution remains the ten-pair PGO comparison above. Control commands and all eight samples are retained as unprofiled-confirmation-*. A disk-space failure before control measurement was retained and retried after compressing owned artifacts; see space-recovery.json.

A separate verbose hardware-counter diagnostic, after one warmup per role, found approximately 1.97 billion additional instructions in checking, 0.98 billion in transforms, and 1.84 billion in C generation/finish. Those instrumented runs are excluded from the primary statistics.

The commit changes semantic type handling as well as sum layout. An executable layout probe using identical fixture definitions measured sizeof(types.Type) changing from 16 to 72 bytes; flat.Node remained 64 bytes. Type interning now canonicalizes child references, and C generation replaces some raw-word identity checks with structural hashing/equality. The compatibility paths also retain duplicated structural-equality calls when the first comparison returns false; disassembly confirms both calls survive optimization in fn_param_compatible and fn_return_compatible. These are concrete follow-up targets, not a measured attribution of the entire increase to larger values or duplicated calls. Overall peak memory decreased in this run.

The bisect log, checkpoint builds/profiles, 22 confirmation samples, generated-output checks, stage counters, layout probe, and disassembly are retained in the bisection directory above. See instruction-bisect.log, confirmation-{metadata,samples,summary}.json, stage-breakdown.json, layout-probe.json, and compatibility-assembly.json. exploratory-log-audit.json records reused exploratory filenames during the parallel parent prebuild; the accepted confirmation has distinct raw logs for every sample. Large generated C artifacts were losslessly compressed to .c.gz with SHA-256 roundtrip checks; generated-c-compression.json records their original paths and hashes. No compiler source or test expectations were changed as part of the bisection.

Tagged-union regression follow-up

The follow-up patch removes repeated work made more expensive by the tagged representation:

  • Cache complete inline-storage walks for sums as well as structs, including concrete generic sums. Reuse positive nested results immediately. Cache negative results across a recursive component only after a complete root walk proves that it contains no inline fixed array.
  • Read existing immutable canonical type references when checking a cached expression, avoiding intermediate copies of the 72-byte type value. Preserve worker ranges and probe overlays; sparse map values use stable copies because map growth can move their backing storage.
  • Remove duplicate semantic-equality calls in three compatibility paths. This cleanup alone did not establish a measurable improvement in exploratory runs.

Both compilers were freshly built from 36232086a5, with the candidate's exact source diff retained in the build artifacts. Each used the same seed, two in-place TCC rebuilds, Apple Clang 21, -mcpu=apple-m1, and fresh matching PGO training on the unchanged fixture. Compiler build paths differed; benchmark executable, input, output, and working-directory paths matched. The PGO option-selection fixes were present in the candidate and do not change this explicit Apple Clang build recipe. No compiler layout, language semantics, or generated output changed.

One warmup per role preceded ten alternating pairs in each mode. All 44 runs succeeded with one positive retired-instruction count per run and byte-identical generated C within each mode. No builds or tests from this task ran concurrently with these comparisons. Parsed input remained 559,990 lines.

Median metric Before, one worker Fix, one worker Before, default Fix, default
Retired instructions, billions 56.5777 55.8777 60.8406 60.1385
Instructions/parsed line 101,033.39 99,783.40 108,645.90 107,392.18
CPU cycles, billions 18.7694 18.6136 27.4735 27.3065
Peak resident memory, MiB 1,117.98 1,118.08 1,011.30 1,010.96
Elapsed seconds 4.4335 4.3891 1.0224 1.0229

The patch used 1.24% fewer instructions with one worker and 1.15% fewer with default workers, with 0/10 regressing pairs in each mode. Median paired differences were -668,456,564 and -704,473,390.5 instructions, respectively. This comparison measures the combined patch; it does not assign the improvement to each edit. Elapsed time and cycles remain contextual measurements on a shared host, not controlled latency claims. The 90,000 instructions/line target remains unmet, and the full tagged-union regression has not been recovered. Do not add percentages from different sessions or compare their medians as a single continuous optimization series.

The baseline executable SHA-256 is 5f8939b958bb5f8c9727202c12d51fad8449ab1fca00346f2cc605e6fbf68c68; the candidate executable SHA-256 is 5e5686330fc08937d3bd8252ba9aacc0dfe75113d1a3a5d388fe2bfa6cf2f9af. Serial generated C has SHA-256 88583a0ca0e4f59877f7c2b7a6cb4260c046c4737a61c665b62af265344bba8d; default-worker generated C has SHA-256 04603de3a32496290c63a567e46069509fd0f985906a40b9696303f14a337158.

Reproduction scripts, exact build/environment metadata, source diffs, fixture inventories, per-run timing/stdout/stderr/status logs, binary hashes, and generated-output checks are in /Users/alex/code/v3-self-90k-artifacts/fix-tagged-unions-20261021/. Use build.py, measure.py, source-a/metadata.json, combined/metadata.json, pr-source-verification.json, and combined-confirmation-{serial,default}*. The PR source adds a helper comment and a reference ownership/visibility test to the measured implementation. Failed and rejected exploratory variants remain separately labeled in this artifact directory and are excluded from the accepted statistics.

Validation rebuilt the compiler in place and passed the type-cache, storage-query, parallel-check, fixed-array escape, and loop-lowering tests. Compiler diagnostics passed 1,848 cases with 5 skipped; PGO tool-selection tests, both generated-C syntax checks, formatting, and Markdown checks passed. Vet reported the same 92 existing diagnostics as the unchanged baseline.

The broad compiler suite finished with 4,138 passed, 40 skipped, and 30 failed. Twenty-eight failing files also failed on the unchanged baseline. The two additional failures were harness effects: the relocated benchmark compiler retained its build-directory path in REPL output, and SOURCE_DATE_EPOCH=0 contradicted a test requiring a recent build timestamp. After rebuilding in the test checkout, all 47 REPL cases passed. The timestamp test passed with the override unset; the unchanged baseline reproduced its failure with the override set.

The cache-refresh failure also varied with compiler placement and macOS /tmp versus /private/tmp aliases. Both baseline and candidate passed this test in the same checkout with the same canonical VTMP. Other failure assertions and primary diagnostics matched the baseline. No tests or expectations were changed to suppress failures; the broad run remains a failed run. Logs and comparisons are retained as final-*, including final-full-audit.json, final-harness-reruns.json, and final-cache-canonical-comparison.json in the artifact directory.

Metric and scope

Use retired instructions per parsed source line to express compiler work; lower is better:

instructions per source line = retired instructions / parsed source lines

Also retain total retired instructions per fixed self-compilation. With the same source fixture, both metrics give the same percentage improvement. Instructions per second depends on hardware, scheduling, and instruction mix, so it is not a compiler-efficiency score.

Count physical lines in the .v and .vh files actually parsed, including builtins and imported modules. Include blank lines and comments: count newline characters and add one for a nonempty final line without a newline. Empty files contribute zero. Use the compiler's parsed .v lines plus parsed .vh lines reports from a separate verbose run of the same workload. Do not use the entry file alone, generated C, or every source file in the repository as the denominator.

Keep the fixture and counting rule fixed. Different lines can require very different amounts of checking, generic instantiation, and code generation; instructions/line is not a fair ranking of unrelated programs. Adding blank lines must not count as an optimization. When updating the fixture, establish a new baseline instead of comparing normalized scores across fixture versions.

Compare on the same machine and architecture. Keep compiler build options identical unless changing the build recipe is the optimization being evaluated; record that change. Record elapsed time, CPU cycles, and peak resident memory too. Fewer instructions do not guarantee lower latency.

Retired instructions are counted for the measured process. Other processes do not contribute their instructions, so an idle host is not required. These are dynamic execution counts: locks, retries, and other runtime paths can still introduce variation. Use VJOBS=1 and repeated pairs to assess consistency. Record competing work as context for elapsed time and cycles; collect latency claims separately under controlled load. Apple documents the per-thread and per-task accounting in XNU Recount.

The workload compiles the full cmd/v compiler through C generation, matching the V stages of v self. It excludes the external C compiler, linker, replacement of ./v, and execution of the resulting compiler. It includes parsing, checking, transforms, and C generation. Use one worker for the instruction comparison; validate default parallelism separately.

Measurement instructions for agents

  1. Start from a clean baseline and preserve its commit. Build baseline and candidate compilers outside measured runs with the same production command and C toolchain:

    env -u VFLAGS -u VEXE SOURCE_DATE_EPOCH=0 V_MACOS_V3_NO_FALLBACK=1 \
      ./v -prod -cc clang self
  2. Keep an unchanged checkout as the source fixture, including its standard library and native dependencies. Copy each built compiler to the same ./v path in that fixture in turn. Use identical source, executable, working-directory, and output paths for both roles. Record the fixture commit and content hashes, binary hashes, OS, hardware, toolchain, build commands, flags, and effective environment. Never measure a stale binary.

  3. From the fixture root, collect the source-line denominator outside measured runs:

    benchmark_out=$(mktemp -d "${TMPDIR:-/tmp}/vself-speed.XXXXXX")
    env -u VFLAGS -u VEXE -u LLVM_PROFILE_FILE VJOBS=1 SOURCE_DATE_EPOCH=0 \
      V_MACOS_V3_NO_FALLBACK=1 \
      ./v -nocache -gc none -prealloc -compile-backend fastc -building-v -v \
        -o "$benchmark_out/self.c" cmd/v \
      > "$benchmark_out/inventory.stdout" 2> "$benchmark_out/inventory.stderr"

    Require success, sum the two parsed-line reports, and preserve the log and source-file list. Verify that baseline and candidate parse the same files and line counts. Repeat this inventory when changing the fixture, target, defines, backend, or other input-compilation flags. Verbose reporting performs extra work; exclude this run from instruction statistics.

  4. Measure this command on macOS arm64, without verbose reporting:

    env -u VFLAGS -u VEXE -u LLVM_PROFILE_FILE VJOBS=1 SOURCE_DATE_EPOCH=0 \
      V_MACOS_V3_NO_FALLBACK=1 \
      /usr/bin/time -l -o "$benchmark_out/time.txt" \
      ./v -nocache -gc none -prealloc -compile-backend fastc -building-v \
        -o "$benchmark_out/self.c" cmd/v \
      > "$benchmark_out/stdout.txt" 2> "$benchmark_out/stderr.txt"
    benchmark_status=$?
    printf '%s\n' "$benchmark_status" > "$benchmark_out/exit-status.txt"
    cat "$benchmark_out/time.txt"

    The measured input is a normal compiler build; -prod optimizes the compiler running the benchmark, not the input workload. -nocache disables V caches, not filesystem caches. V_MACOS_V3_NO_FALLBACK=1 prevents silent fallback to the legacy compiler. Reuse one output C path across all paired runs and keep distinct timing/stdout/stderr logs.

  5. Run one warmup per role, then ten measured pairs, alternating AB, BA, AB, BA, and so on. Run only one benchmark at a time. Do not wait for an idle host or pause unrelated work. Other builds and tests do not invalidate process instruction counts. Keep native support objects warm for both roles and record that state. Disable tracing and profiling. Label elapsed time and cycles collected under uncontrolled load as contextual measurements.

  6. Require exit status zero and exactly one positive instructions retired counter per sample. Preserve failed samples but do not count them as a completed comparison. If hardware counters are missing or zero, report them as unavailable; do not substitute elapsed time.

  7. Verify byte-identical generated C for an internal optimization, allowing only recorded build metadata differences. Run the relevant correctness tests and rebuild the working ./v.

  8. Divide each run's retired instructions by the fixed parsed-line count. Report the denominator, median instructions/line, total instructions, percentage reduction, median paired instruction difference, number of pairs that regressed, elapsed time, cycles, and memory. Retain all raw samples and reproduction metadata. Investigate inconsistent pairs; never select the fastest run or compare medians from separate sessions as proof of a small gain.

On another platform, use an available hardware retired-instruction counter with the same scope and protocol; do not compare its absolute counts with this machine's results.

Current self-compilation fixture

New comparisons use a detached checkout of merged master 2d1367e589ed30c03325e586a8ddfd3bc96c8c3d, including its standard library. It contains 559,990 parsed physical lines in 562 .v files, with no .vh files. The 90,000 instructions/line target is 50,399,100,000 instructions for this fixture. Keep this source and denominator fixed across baseline and candidate runs.

The older 257b fixture below is incompatible with the merged compiler's sum-type representation: it emits incomplete recursive C union fields for its old compiler types. A C-file hash match alone did not catch this. The new fixture's generated C passes Clang syntax checking. Validate generated C when changing the fixture or compiler representation; do not accept counts for an invalid output. This establishes a new baseline; do not compare normalized scores across fixtures.

An exploratory three-pair run measured 105,611.36 instructions/line for merged master and 105,762.28 for local-storage lookup guards. Generated C matched in every run. The guards showed no convincing gain and are excluded from the optimization. Three pairs do not establish a small regression or a completed ten-pair comparison. Logs remain under /Users/alex/code/v3-self-90k-artifacts/guard-probe*.

The new fixture is /Users/alex/code/v3-self-90k-fixture. Its 8,498-file source manifest uses the encoding described below and has SHA-256: f53a9b162657ba667fde1d8a00cd6f59c0f7e6c7f847985894d61189122a2966. Its source inventory, syntax-check logs, build logs, binaries, and instruction samples remain in /Users/alex/code/v3-self-90k-artifacts/.

Semantic interning cache comparison

The first source optimization on master 9ca360c4ff adds an atomic cache of immutable canonical semantic types. Hits verify complete structural equality before bypassing recursive hashing, locking, and map lookup. Promotion clears the cache before worker arenas are released. The checker also uses the cheap fingerprint for its existing type-name cache, retaining its full structural-equality check.

Both roles use successful Clang 23 PGO builds with -mcpu=apple-m1, the same build recipe, and the unchanged 559,990-line fixture. The candidate is the first cache variant, before the subsequent source-spelling and child-fingerprint experiments.

Median metric Before, one worker Cache, one worker Before, default Cache, default
Retired instructions 57,045,612,737.0 55,295,951,631.0 61,027,236,671.0 59,242,819,343.0
Instructions/parsed line 101,868.98 98,744.53 108,979.15 105,792.64
CPU cycles, billions 27.0600 26.8994 28.7436 28.6683
Peak resident memory, MiB 1,118.17 1,118.55 1,011.12 1,014.97
Elapsed seconds 10.6821 9.4104 2.7068 2.6945

Retired instructions decreased 3.07% with one worker and 2.92% with default parallelism. All ten pairs in each mode improved. Median paired instruction differences were -1,799,425,264 and -1,775,404,206.5, respectively. All 44 samples succeeded and generated byte-identical C within each mode. C passed Clang syntax checking. Elapsed time and cycles were collected under uncontrolled host load. The 90,000 instructions/line target is not met.

The 151 type tests passed, including borrowed-payload mutation, concurrent growth, and arena promotion checks. Validation of the final cache bundle is recorded below. Commands, environment, raw counters, and output checks are retained as interner-cache-matched-* and interner-cache-syntax-validation.json in the artifacts directory. The baseline executable SHA-256 is 575ec55f8f553cfea6bff81ad937192687a840d84cc0f7e8832327804be5b8b7; the candidate executable SHA-256 is 22e35698c36f05c7009b2516cda9270985366404230fabdf2399c16f178a3572.

Follow-up cache comparison with Apple Clang 21

A fresh comparison on master 9ca360c4ff used Apple Clang 21 PGO for both roles and the same 559,990-line fixture. The candidate combines the semantic interning cache with source-spelling content fingerprints, shallow semantic child fingerprints, and a reference receiver for Type.source_type_id. Fingerprints still require complete equality checks before reuse.

Median metric Before, one worker Cache, one worker Before, default Cache, default
Retired instructions, billions 59.0643 57.5221 63.1715 61.5222
Instructions/parsed line 105,473.91 102,719.79 112,808.22 109,862.98
CPU cycles, billions 29.1419 28.4944 31.1445 30.3943
Peak resident memory, MiB 1,117.54 1,118.23 1,007.91 1,010.11
Elapsed seconds 33.9576 34.9625 6.2985 6.6990

The combined change used 2.61% fewer instructions in both modes, with 0/10 regressing pairs in each. Median paired differences were -1,529,766,769 instructions serial and -1,646,091,022.5 default. All 44 runs succeeded and emitted byte-identical C within each mode. Elapsed time and cycles reflect uncontrolled host load. This is a matched source-change comparison; its absolute scores must not be compared with the Clang 23 sessions. The 90,000 instructions/line target is still unmet.

Raw samples, binary hashes, commands, environment, and output checks are retained as reference21-content-matched-* in the artifacts directory above. This bundle comparison does not establish an independent benefit from each additional fingerprint change.

Validation of this exact cache bundle completed with 1,848 compiler-error cases passing and 5 skipped; all 151 type tests, source-spelling tests, and PGO/self-build tool tests passed. Formatting, Markdown checks, and vet completed; vet reported existing documentation warnings. The broad compiler suite finished with 4,140 passed, 40 skipped, and 28 failed. All 28 failing files also failed with the unchanged 9ca360c4ff baseline binary. Assertion locations and primary compilation diagnostics matched; the cache-refresh test required the same checkout path to reach the same assertions. No tests or expectations were changed to suppress these failures. This is not a full-suite pass. Raw logs and comparisons are retained as content-child-compiler-tests.*, baseline-failed-subset-*, and baseline-cache-same-root.*.

LLVM toolchain lookup comparison on latest master

Measured on 2026-10-21 with compiler sources from master 9ca360c4ff, the toolchain lookup fix at 8a6e8a32dc, and the same 559,990-line fixture. Both compilers were rebuilt in place with the command above, using Homebrew Clang 23.1.0 and -cflags '-mcpu=apple-m1'. The baseline again fell back after the LLVM 21/23 profile-format mismatch; the candidate completed PGO. This compares the tool lookup fix on the same compiler sources, so upstream changes between master versions are not attributed to that fix.

Median metric Before, one worker After, one worker Before, default After, default
Retired instructions, billions 68.9401 56.3825 73.8913 60.6981
Instructions/parsed line 123,109.57 100,684.76 131,951.04 108,391.44
CPU cycles, billions 23.0552 20.1368 32.7966 29.9206
Peak resident memory, MiB 1,116.04 1,118.15 1,009.52 1,014.15
Elapsed seconds 5.4194 4.7996 1.2632 1.1951

Instruction reductions were 18.22% serial and 17.85% default, with 0/10 regressing pairs in each mode. Median paired instruction differences were -12,579,586,975.5 serial and -13,218,242,894.5 default. The 90,000 instructions/line target remains unmet. All 44 runs succeeded and generated byte-identical C within each mode; both outputs passed Clang syntax checking. Elapsed time and cycles were collected under uncontrolled host load. The PGO, ordinary self-build, and cache-bypass tool tests passed, as did the hello-world smoke test.

Raw samples, complete environments, commands, build logs, binary hashes, syntax checks, and validation logs are retained as latest2-* in the artifacts directory above. Generated C hashes match the preceding master comparison below. SHA-256 values:

baseline executable, latest2-before-v
8cdaf2fd18d6abd30e12e67953914abfa1d0b3e733211c4509b316095de71b9b
candidate executable, latest2-after-v
575ec55f8f553cfea6bff81ad937192687a840d84cc0f7e8832327804be5b8b7
merged training profile
93e991a1f4f6952c4645d0dab67a4da1a897fca48c647dea5dcc5c53f9f1db1b

Compiler revision check on identical input

A separate 2026-10-21 comparison held the 559,990-line fixture, source-file list, Apple Clang 21, production settings, and PGO recipe fixed. The older binary uses compiler sources at 257b514a; the newer binary uses merged sources at 2d1367e589. Each build trained on its own revision's compiler source, following the same self-build recipe. The older binary successfully compiles the newer fixture to C, which makes this direction of comparison possible.

Median metric Older compiler Merged compiler
Retired instructions 54,646,536,102.5 58,840,900,876.5
Instructions/parsed line 97,584.84 105,074.91
CPU cycles, billions 23.5851 25.1845
Peak resident memory, MiB 1133.02 1117.52
Elapsed seconds 7.9881 8.9251

The merged compiler executed 7.68% more instructions between medians, with more work in 10/10 pairs. The median paired increase was 4,174,866,314 instructions. All 22 runs succeeded and each role produced repeatable C. Both C outputs passed Clang syntax checking. They intentionally differ across roles because compiler revisions changed sum-type representation; this is a revision diagnostic, not an output-preserving internal optimization comparison. It establishes increased work for these compiler builds without isolating a particular commit. Elapsed time and cycles were collected under uncontrolled host load.

Thus the earlier reporting mixed an unprofiled baseline with a PGO result, but a compiler-version increase also exists on matched input. The historical 95,877.04 result still uses the older 560,382-line fixture; attributing the remaining fixture effect requires a separate matched check. Do not explain the entire discrepancy as denominator changes or assume that no regression exists.

Logs, inventories, counters, commands, environments, and syntax checks are retained as revision-diagnostic-* in the artifacts directory above. The 562-file inventory matches exactly between roles. SHA-256 values:

older compiler executable
e554d76a9ad101c03c74864b5d428cef6f7b1df29fd11dad8ab43e74e60cbde4
merged compiler executable
7aab1e75525e5966566d7fe30debb543aaaeea68159dcd94047b46b7ada3788f
older compiler generated C on the current fixture
abb6372f6afc7892a15db0843ad4cca9fa2ca638ef331704866d59fb6d72d50d
merged compiler generated C on the current fixture
88583a0ca0e4f59877f7c2b7a6cb4260c046c4737a61c665b62af265344bba8d

Matched Clang 23 comparison before the latest master rebuild

Measured on 2026-10-21 with compiler sources from merged master 2d1367e589 and the fixed 559,990-line fixture above. Both roles selected Homebrew Clang 23.1.0 and -mcpu=apple-m1. The baseline selected Apple's LLVM 21 profile merger, which rejected Clang 23's version-11 raw profile and triggered an ordinary production build. The candidate selected Clang 23's merger and completed PGO. Both final binaries contain no profiling runtime instrumentation.

The same production self-build command was used in each checkout, outside measured runs:

env -u VFLAGS -u VEXE -u LLVM_PROFILE_FILE -u VJOBS SOURCE_DATE_EPOCH=0 \
  V_MACOS_V3_NO_FALLBACK=1 ./v -prod -cc /opt/homebrew/opt/llvm/bin/clang \
  -cflags '-mcpu=apple-m1' -o /absolute/path/to/role-v self

Each mode used one warmup per role and ten alternating measured pairs. All 44 runs succeeded; generated C matched byte for byte within each mode. Elapsed time and cycles reflect shared-host load and do not establish a latency improvement.

Median metric Before, one worker After, one worker Before, default After, default
Retired instructions, billions 69.8344 56.8402 74.4558 61.1362
Instructions/parsed line 124,706.52 101,502.13 132,959.12 109,173.73
CPU cycles, billions 33.1496 28.9486 33.3547 30.3399
Peak resident memory, MiB 1116.16 1118.05 1008.88 1010.32
Elapsed seconds 21.2254 18.6263 5.4424 5.5684

The median instruction reductions were 18.61% serial and 17.89% default, with 0/10 regressing pairs in each mode. Median paired differences were -12,984,267,344.5 and -13,337,504,752 instructions, respectively. The 90,000 target was not reached. Logs and samples are clang23-matched-{serial,default}* in the artifacts directory above; clang23-before-build.log records the version mismatch and fallback, while clang23-after-build.log records the successful PGO build.

The optimized count is higher than the historical 95,877.04 result below, but these sessions do not isolate a compiler regression. The historical result used Apple Clang 21, compiler sources at 257b514a, and a different source fixture. Both compiler implementation and input changed after the tagged-union merge. The fixture has only 392 fewer lines, so the denominator alone does not explain the difference. Isolating the causes requires matched comparisons; comparing these two sessions cannot attribute the increase. In particular, 124,706.52 is the current unprofiled baseline, not the optimized result.

SHA-256 values for this comparison:

baseline executable, clang23-before-v
1541c85d77b10337467caacbc6cf67314a1635d78ce5c9e20e1b8b7f1fb6d454
candidate executable, clang23-after-v
c5ad981e88dc2f22fd0bd97042094e89a55ac85ee4dcd75e2ffa2ac86714edaa
generated C, every serial run
88583a0ca0e4f59877f7c2b7a6cb4260c046c4737a61c665b62af265344bba8d
generated C, every default-worker run
04603de3a32496290c63a567e46069509fd0f985906a40b9696303f14a337158

Historical measurement status

The PGO comparison below records process instruction counts under shared-host load on the old fixture. The earlier quiet-host gate was too strict: unrelated compilations do not invalidate these instruction measurements. All ten paired differences favored PGO in each mode, with matching source inventories and generated C. Elapsed times and cycles reflect uncontrolled host load and do not establish a latency improvement. These results describe the older compiler and fixture, not the current self-compilation baseline.

The 2026-10-21 attempt that waited 120 seconds for an idle host started no measurements. Its logs remain in /Users/alex/code/v3-self-speed-artifacts/quiet-20261021-attempt1.log and quiet-20261021-attempt1-guard.json; an idle host is no longer an acceptance requirement.

Historical PGO instruction comparison

Measured on 2026-10-20. Baseline: merged master 257b514a303f4d302ed23bbac1d51a2ca9624a66. Candidate: that commit with the Clang production self-build changes later merged in #30962. The compiler source compiled into both binaries is unchanged; the intentional build difference is profile-guided optimization (PGO). The candidate was rebuilt and remeasured after correcting atomic profile collection and isolating the training environment.

Both used the production command above, with Clang's large-unit -O2 -flto settings, GC disabled, preallocation, and FastC included. The baseline made one ordinary optimized compiler. The candidate made a compiler with atomic profile counters, trained it on uncached self-compilation with default workers and VJOBS=1, merged the profiles, and rebuilt with -fprofile-use. The compiler contains no profiling instrumentation. This comparison evaluates the resulting compiler's execution, not the cost of producing it.

The session measured 16.87% fewer instructions with one worker and 16.40% fewer with default workers. These are instruction-count reductions, not elapsed-time improvement claims. All 44 runs, including warmups, exited successfully and generated byte-identical C. The fixed input contains 560,382 parsed physical lines in 555 .v files, with no .vh files. The baseline and candidate report the same source closure and line count. Normalizing the retained instruction samples by this denominator gives the same 16.87% and 16.40% reductions.

Median metric Baseline, one worker PGO, one worker Baseline, default PGO, default
Retired instructions, billions 64.6278 53.7278 68.9180 57.6124
Parsed physical source lines 560,382 560,382 560,382 560,382
Retired instructions/source line 115,328.08 95,877.04 122,983.97 102,809.22
Elapsed seconds 25.6428 21.7045 3.2356 2.9937
CPU cycles, billions 28.4689 25.4029 30.6580 27.2978
Peak resident memory, MiB 1129.77 1131.19 998.66 1004.67

Median paired instruction differences, candidate minus baseline: -10,938,181,556 with one worker and -11,360,585,289.5 with default workers. Instruction regressions: 0/10 pairs in each mode, for both total instructions and instructions per source line.

Process snapshots recorded unrelated compilations elsewhere on the shared host. Retain them as context for the elapsed-time and cycle measurements. Compare subsequent compiler changes against a freshly built baseline in the same measurement session, preserving the fixed fixture and all raw data.

Production self-build behavior

On non-Windows hosts, ./v -prod -cc clang self uses PGO when llvm-profdata is available. The profile merger is searched in Clang's reported installation directory, then beside its resolved executable, then in PATH and through xcrun on macOS. This avoids a fallback when a different LLVM release cannot read the selected compiler's raw profile format. This spends more time building the production compiler: two C compilations plus training, rather than one C compilation. Ordinary ./v self keeps its existing build path. Explicit -parallel-cc, -no-prod-options, explicit -cflags profile options, unavailable profiling tools, and other C toolchains retain ordinary builds. A failed PGO step falls back to an ordinary production build.

Training clears inherited VFLAGS, VEXE, and LLVM_PROFILE_FILE, disables caches and legacy fallback, and covers both default worker dispatch and the serial path. Profiling uses atomic counter updates so worker threads do not lose increments. VJOBS=1 or -no-parallel trains only the serial path. Workspaces are private to each invocation and removed afterward; existing workspaces and the caller's profile destination are preserved. Profile paths containing spaces remain a single C compiler argument. The final effective -cc selects the PGO toolchain; command-line options override compiler choices inherited through VFLAGS. Split -cc clang and equals-form -cc=clang options have the same last-option-wins behavior.

Reproduction metadata

  • Host: Apple M5 Max, 18 CPU cores, 128 GiB RAM; macOS 26.5, build 25F71, arm64.
  • Toolchain: Apple Clang 21.0.0 (clang-2100.1.1.101), Command Line Tools; matching llvm-profdata.
  • Fixture: clean detached checkout of the baseline commit, including its standard library. Bundled TCC: 1d0ad0ecf70a91a1df64cebf215e683b1d5aedb5; native support objects were warm.
  • Fixed executable and working directory: /Users/alex/code/v3-self-speed-fixture/v and its root.
  • Fixed output: /Users/alex/code/v3-self-speed-artifacts/self.c.
  • Environment: SOURCE_DATE_EPOCH=0, V_MACOS_V3_NO_FALLBACK=1; VFLAGS, VEXE, and LLVM_PROFILE_FILE were unset. VJOBS=1 for serial measurements and unset for default workers. CC, CFLAGS, and LDFLAGS were unset. Locale: LANG=LC_ALL=C.UTF-8; SDK: Command Line Tools MacOSX.sdk.
  • Elapsed seconds use Python time.perf_counter() around /usr/bin/time -l; the original two-decimal time output is also retained. Older JSON logs include instructions/second as historical data; do not use that field to score compiler efficiency.
  • Raw timing, stdout, stderr, exit statuses, process snapshots, scripts, build logs, environment, binaries, and JSON samples remain in /Users/alex/code/v3-self-speed-artifacts/. The current comparison uses the review-serial and review-parallel logs and JSON files. Source-line inventories and normalized samples are retained as stage-probe.log, loc-inventory.stdout, and loc-normalized.json.
  • The validation build inherited LLVM_PROFILE_FILE pointing to an existing caller profile; PGO completed successfully and preserved that file. The merged training profile is retained as review-code.profdata, with SHA-256: f9a836d2f0ee04f0f4929d797a77fe61bfd5b0ca03bfff56acf1e0069186a995.

SHA-256 values:

baseline executable
5e34c408511cbc55280044e1dd8cd8ff8b4bf78454940d72b9d5346ac08de490
candidate executable
e554d76a9ad101c03c74864b5d428cef6f7b1df29fd11dad8ab43e74e60cbde4
generated C, every run in both modes
46a580197213a2619173a5a17a2f0dac81fd56b14306531e23a0122016e2e3b7
fixture source manifest, 8430 files
22eee10913aa354b01af6dfcf92a521c95f7c5d13c4ebba69bab7b4dd1abdef7

The fixture manifest contains sorted Git-tracked .v, .vsh, .c, and .h files under cmd/v and vlib, with each entry encoded as relative path, NUL, hexadecimal file SHA-256, and newline. The manifest hash covers those bytes. The task worktree is /Users/alex/code/v3-self-speed. The retained review-source.diff records the tool changes used to build the measured candidate.

All paired samples

The warmup pair is excluded from every statistic. Measured order alternated baseline/candidate for odd pairs and candidate/baseline for even pairs. Counts are integers; elapsed seconds below are rounded for display only.

One worker:

Pair Baseline instructions PGO instructions Baseline s PGO s
warmup 64,797,713,503 53,466,693,440 10.0697 7.8065
1 64,563,117,272 53,457,419,214 17.3451 9.5121
2 64,357,789,151 53,434,337,434 9.6554 8.4718
3 64,608,456,228 53,462,161,257 8.7556 10.0894
4 64,768,758,757 53,534,779,015 16.6670 12.0576
5 64,586,520,655 54,052,280,113 46.9252 67.1949
6 64,932,154,872 54,166,175,890 42.3820 48.6051
7 64,805,895,480 53,852,984,085 42.0541 23.6558
8 64,609,248,554 53,703,915,249 25.0856 30.6926
9 64,646,316,518 54,043,118,299 26.2001 29.3813
10 64,843,362,579 53,751,619,879 31.3356 19.7531

Default workers:

Pair Baseline instructions PGO instructions Baseline s PGO s
warmup 69,204,428,535 57,795,598,290 2.9232 2.9259
1 69,362,428,389 57,674,395,599 3.9125 3.3833
2 69,217,849,819 57,688,811,832 3.8882 3.3916
3 69,100,209,757 57,432,125,863 3.5501 2.8208
4 68,588,463,206 57,304,532,710 3.3423 2.4053
5 68,736,420,274 57,415,457,216 3.1290 2.3903
6 68,914,207,957 57,478,312,333 2.8508 2.7886
7 68,921,795,238 57,767,190,237 2.8897 3.1666
8 68,751,695,790 57,601,275,877 2.0765 1.9713
9 68,860,826,104 57,623,599,267 2.6765 3.6652
10 69,169,667,117 57,769,459,596 4.4418 4.6444