diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d001519e76..a9643d666d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: check-added-large-files # Retain the exact lossless cross-platform performance evidence archives. - exclude: ^docs/benchmarks/((executor-copy-audit|bounded-read-ahead)-20261101|(directory-task-slots|packed-visit-snapshots|bounded-match-results|useful-match-workers|direct-evaluator-workers|derived-content-handoff|immutable-default-catalogs|prepared-diff-configuration|prepared-hash-operands|aggregate-comparison-totals|byte-shingle-representations|archive-input-buffers)-20261102|(digest-and-record-storage|caller-owned-streams|direct-source-materialization|hash-field-binding|hash-field-binding-tag|forward-cursor-skipping|raw-record-reuse|similarity-prefix-view|similarity-reference-reuse|owned-word-moves|smaller-shingle-probes|native-zip-seek-evaluation|encoded-record-reuse|threshold-aware-similarity)-20261103|(column-row-reuse|word-set-insertion|field-context-owned-results|selected-summary-output|fixed-summary-metrics|direct-summary-cells|compact-comparison-entries|comparison-category-sequence|native-name-paths|uniform-native-case|compact-traversal-metadata|eager-native-paths|native-case-experiments)-20261104|(folded-ascii-globs|bulk-directory-metadata|comparison-retention-followups|comparison-storage-followups|archive-prefix-reuse|archive-directory-range|archive-directory-allocation|archive-directory-observations|native-symlink-observations|native-metadata-followups|symlink-target-demand|native-pgo|readlink-short-target|cctz-empty-format|readlink-input-path|readlink-owned-transfer|archive-lookup-trials|archive-member-keys)-20261105|archive-directory-child-capacity-20261106|shared-stage-workers-20261107|archive-index-insertion-20261107|transactional-rg-records-20261107|immutable-zip-locator-evaluation-20261107|queued-read-cancellation-20261107)\.json\.gz$ + exclude: ^docs/benchmarks/((executor-copy-audit|bounded-read-ahead)-20261101|(directory-task-slots|packed-visit-snapshots|bounded-match-results|useful-match-workers|direct-evaluator-workers|derived-content-handoff|immutable-default-catalogs|prepared-diff-configuration|prepared-hash-operands|aggregate-comparison-totals|byte-shingle-representations|archive-input-buffers)-20261102|(digest-and-record-storage|caller-owned-streams|direct-source-materialization|hash-field-binding|hash-field-binding-tag|forward-cursor-skipping|raw-record-reuse|similarity-prefix-view|similarity-reference-reuse|owned-word-moves|smaller-shingle-probes|native-zip-seek-evaluation|encoded-record-reuse|threshold-aware-similarity)-20261103|(column-row-reuse|word-set-insertion|field-context-owned-results|selected-summary-output|fixed-summary-metrics|direct-summary-cells|compact-comparison-entries|comparison-category-sequence|native-name-paths|uniform-native-case|compact-traversal-metadata|eager-native-paths|native-case-experiments)-20261104|(folded-ascii-globs|bulk-directory-metadata|comparison-retention-followups|comparison-storage-followups|archive-prefix-reuse|archive-directory-range|archive-directory-allocation|archive-directory-observations|native-symlink-observations|native-metadata-followups|symlink-target-demand|native-pgo|readlink-short-target|cctz-empty-format|readlink-input-path|readlink-owned-transfer|archive-lookup-trials|archive-member-keys)-20261105|archive-directory-child-capacity-20261106|shared-stage-workers-20261107|archive-index-insertion-20261107|transactional-rg-records-20261107|immutable-zip-locator-evaluation-20261107|queued-read-cancellation-20261107|archive-btree-historical-decision-20261107)\.json\.gz$ - id: check-merge-conflict - id: check-yaml # BINARY_TYPES below: archive containers are binary but can START with text (a phar begins with diff --git a/TODO.md b/TODO.md index a49f11ebbc..4590d093e7 100644 --- a/TODO.md +++ b/TODO.md @@ -1431,10 +1431,11 @@ this as intentional accounting. new formatter-parent screens. Do not adopt the current loop: isolated wide/root listings repeatedly regress around 30-44%, despite major deep-root gains. Any future combined layout/loop revision needs a new hypothesis and independent qualification. -- [ ] O19k: Independently measure an Abseil B-tree for the private immutable archive node map. - Audit mutation-only indexing, owned returned sources/metadata and nonescaping iterators. - Keep the pinned default transparent three-way string comparator; include large Node - insertion/relocation, root/flat/chain and RSS costs before testing integration. +- [x] O19k: Evaluate the private archive B-tree substitution without adoption. Reconcile the + earlier completed trial and tracking gap in [the historical decision](docs/benchmarks/archive-btree-historical-decision-20261107.md). + Construction gains do not erase wide/deep query costs; old source/settings and incomplete + contemporaneous image receipts are explicit. No new runtime or current-head claim. A future + directory-specific index is a distinct design, not an unchanged retry of this policy. - [x] O19i2: [Qualify a bounded archive direct-child capacity hint](docs/benchmarks/archive-directory-child-capacity-20261106.md). The count fits existing `Node` padding; the one root count adds 8 bytes per archive FS. Three balanced sessions on Mac and Linux show large flat/wide-root listing wins, neutral diff --git a/docs/benchmarks/archive-btree-historical-decision-20261107.json.gz b/docs/benchmarks/archive-btree-historical-decision-20261107.json.gz new file mode 100644 index 0000000000..6c3c3a1b63 Binary files /dev/null and b/docs/benchmarks/archive-btree-historical-decision-20261107.json.gz differ diff --git a/docs/benchmarks/archive-btree-historical-decision-20261107.md b/docs/benchmarks/archive-btree-historical-decision-20261107.md new file mode 100644 index 0000000000..9f8a5af8b2 --- /dev/null +++ b/docs/benchmarks/archive-btree-historical-decision-20261107.md @@ -0,0 +1,109 @@ +# Archive B-tree: reconcile the preserved historical decision + +Keep the private archive node index as `std::map`. The earlier O19k trial replaced only that +container with `absl::btree_map`, using its default transparent string comparator and a direct +Bazel dependency. Its construction and some flat/chain workloads improved, but most wide/deep +directory queries slowed down on both hosts. Those costs prevent adoption of that implementation. + +The TODO and analysis still described O19k as unmeasured after this experiment had finished. +This report corrects that tracking gap and publishes the retained observations. It runs no new +benchmark, changes no runtime and does not present old observations as current-main qualification. + +## Source and lifetime audit + +The preserved candidate and clean parent worktrees identify base +`45f4c5e65bfb7de5d0facf8bcbdca1d28320ae4a`. Their patch touches only the archive header and its +direct dependency. `Index` is the only mutation phase: inserted node references are consumed before +ancestor insertion, and an existing ancestor's count is updated immediately before breaking. +Parent keys own their text. Immutable read iterators stay within their calls; metadata, listings, +symlink text and deferred member sources return owned values or own their retained source/key. +No escaping node reference was found. Relocation of large nodes and short string keys is legal +under that contract, but its performance is workload-dependent. + +The default `btree_map` comparator matters: pinned Abseil supplies its transparent +three-way string comparison adapter. This was not a trial accidentally forcing `std::less<>`. +There were no borrowed-key changes, ancestor hints, interning, new workers or decoder policies. + +## Preserved protocol + +There are three sessions on each host. Session orders are parent/candidate/parent, +candidate/parent/candidate, then parent/candidate/parent. Each invocation has nine raw repetitions; +the fastest seven `real_time` values are averaged per case. When a role appears twice in a +session, its two retained means are combined with a geometric mean. Divide candidate by parent +within each session, then take the median of those three ratios per case. Negative elapsed-time +change means faster. + +The 28 construction cases include parsing, index construction and destruction. The 88 directory +cases separately measure ordinary listings and complete metadata listings across flat, wide, +deep and chain shapes, including 10 through 100,000 members where the fixture permits. This is +single-threaded public archive API work, not native CLI throughput or multiworker scaling. +The Linux runner pins CPU 4, in its available ten-CPU set; Mac reports eighteen available logical +CPUs and has no affinity enforcement. Driver paths and JSON context retain the exact invocations. + +Each host's corrected `-v2` population retains all eighteen unique JSON runs and eighteen logs: +nine construction and nine directory invocations. Every case has nine raw observations and no +benchmark error. Initial populations overwrote one of the repeated roles' output files; preserve +them as exploratory data only. Do not combine them with the corrected population or call their +remaining files complete controls. The corrected driver fixes that recording defect; it is not +an attempt to select only favorable observations. + +## Recomputed historical results + +Each cohort applies its statistic to the per-case median ratios described above. Faster-case +counts include every case. Geometric means alone conceal the slower majority of wide/deep work. + +| Workload | Cases | Mac median | Mac geometric mean | Mac faster | Linux median | Linux geometric mean | Linux faster | +| --------------------- | ----: | ---------: | -----------------: | ---------: | -----------: | -------------------: | -----------: | +| Construction | 28 | -7.06% | -6.09% | 25/28 | -0.66% | -0.96% | 18/28 | +| All directory queries | 88 | +1.92% | -6.58% | 39/88 | +2.60% | -2.73% | 41/88 | +| Flat | 10 | -8.49% | -6.27% | 8/10 | -5.10% | -4.83% | 7/10 | +| Wide | 30 | +3.71% | -2.30% | 10/30 | +6.20% | +1.79% | 10/30 | +| Deep | 30 | +2.35% | -5.70% | 10/30 | +7.87% | +3.78% | 10/30 | +| Chain | 18 | -3.13% | -14.80% | 11/18 | -8.29% | -18.06% | 14/18 | + +Examples include Linux deep/one-directory/1,000 members at about +18.8% and wide/one-directory/ +1,000 at +14.8%. Mac deep/ten-directories/100,000 is about +8.6%. Strong chain/root cells improve +about 35-43%; they do not cancel those costs. The all-cell CSV and three session ratios remain +in the capsule, so a reader can inspect individual signs rather than infer them from a surface +plot or an aggregate geometric mean. + +## Provenance and verification limits + +The original decision note reports optimized archive filesystem tests passing on both hosts, +and benchmark image growth of about 39 KiB. That +note is retained verbatim. It did not freeze a complete contemporaneous compiler-action/source/ +image manifest. Later worktree/image identities collected during this reconciliation are labeled +**later captures**; they cannot prove the exact bytes used in the earlier timed invocations. +Raw JSON contexts do identify their executable paths, hosts, dates and release benchmark library. +No full product binary size, full target suite, sanitizer matrix, RSS or current-head CI claim is +made for this historical trial. + +The current base `d8cf61cccd4b0efed5058bf6fa45228e47162b48` differs from the old parent: archive +ownership/index-return handling, directory scanning and release/tool linking configuration have +changed. The preserved diffs make that explicit. Do not transfer the old percentages to current +production or add them to later gains. They are sufficient reason to keep the old policy +unselected, not proof that every possible new B-tree/index design is slower. + +Before discovering the completed records, a new private worktree was created and a mixed +short/long-key duplicate-inode regression was drafted. Only formatting and source transfer ran; +no candidate compile, test or timing started. That draft stays private and unqualified. Reconcile +existing evidence before spending another measurement cycle. A future directory-specific child +index or range representation is a distinct proposal, with its own lifetime, retained-byte and +construction/query controls, rather than another unchanged global-container swap. + +## Download and reproduce the analysis + +[Lossless historical capsule](archive-btree-historical-decision-20261107.json.gz): 155 individually hashed records, +43,505,320 decoded bytes; 2,205,832 compressed bytes. +SHA-256: `e609682737ebd09c27b5760e3dd811e55f7e54fbc9c1b852c7e21f8dcf983df0`. + +Restore each JSON `records` entry according to `encoding`, then verify `bytes` and `sha256`. +It retains both initial and corrected populations, every raw log, the original decision note, +runner/analysis programs, source patch and later source/configuration receipts. Executable images +stay private; later identities are expressly not a replacement for missing original receipts. + +`analyze_o19k_bracketed.py` reproduces the original analysis after adjusting its two input paths. +`prepare-historical-report.py` additionally checks all corrected case/repetition counts, preserves +session ratios and emits `all-case-comparisons.csv`. Neither command rebuilds or times a binary. +Keep the exact original runtime policy rejected; require a substantive new design and fresh +both-host qualification before considering adoption. diff --git a/docs/performance-analysis.md b/docs/performance-analysis.md index e98c552108..a6eba9ebea 100644 --- a/docs/performance-analysis.md +++ b/docs/performance-analysis.md @@ -4581,11 +4581,10 @@ The following remain independent experiments, not promised speedups: Deep-root gains do not erase repeatedly slower wide/root listing controls. Preserve the old Linux full trial and deliberately partial Mac population separately from the new complete formatter-parent screens. A future loop/layout combination needs fresh qualification. -- O19k: replace only the private immutable archive node map with Abseil's B-tree. Index is the - sole mutation phase; later APIs return owned values/sources and do not retain node iterators. - Verify that audit and insertion/large-Node relocation costs on both systems. The pinned default - string comparator enables transparent three-way lookup; explicitly passing `less<>` would skip - that specialization. Keep this independent of descendant skipping before testing integration. +- O19k: [historical B-tree evaluation reconciled without adoption](benchmarks/archive-btree-historical-decision-20261107.md). + Construction and chain gains do not erase wide/deep query costs. Original source/linking settings + differ from current production and contemporaneous image receipts are incomplete; do not claim + current-main qualification. A directory-specific child index remains a distinct future design. - O19i2: [qualified bounded direct-child capacity hint](benchmarks/archive-directory-child-capacity-20261106.md). The count fits existing `Node` padding, while the root count adds 8 bytes per archive FS. Mac and Linux measurements show large flat/wide-root listing gains, neutral construction, and @@ -4657,8 +4656,9 @@ No production index, CLI, comparator, safety setting or ownership behavior chang Transparent `lower_bound` plus hinted ancestor insertion remains an independent unmeasured candidate. Preserve duplicate replacement, synthesized flags, child counts and inode order. Measure public construction and destruction including parsing, not only hot listings whose -index setup is untimed. A future B-tree audit still must account for relocated small-string -map keys and escaping references. +index setup is untimed. The [historical B-tree audit and trial](benchmarks/archive-btree-historical-decision-20261107.md) +found no escaping node references but rejected the container policy after wide/deep costs. +Its older source/settings and missing contemporaneous image receipts do not establish current-head qualification. O29 examines borrowed normalized member-key views for immediate stat/access/readlink/cache lookups. Normalization only selects a contiguous input subview. Stored index keys and both @@ -4888,3 +4888,18 @@ Retain all three sources and phase-specific tests in both hosts; explicit-stop focused optimized/ASan and Linux MSan/TSan pass, with no final full runtime CI claim. Publish only report/evidence; retain broader O18 byte backpressure, fairness, ordered publication and a lower-cost cancellation redesign as open work. + +### O19k: reconcile the historical B-tree trial without adoption + +[The historical decision](benchmarks/archive-btree-historical-decision-20261107.md) recovers the completed both-host trial that +tracking still called unmeasured. Replacing only the private node map improved construction +medians about 7.06% on Mac and 0.66% on Linux, but directory medians cost 1.92%/2.60%; most wide +and deep queries regress, including Linux individual costs near 15-19%. Keep the container swap +unselected rather than choosing by a geometric mean dominated by strong chain/root cells. + +All corrected three-session nine-repeat/seven-retained raw data, overwritten-control initial +screens, source patch and analysis are preserved. Historical base `45f4c5e` differs from current +archive ownership/scanning and release linking; original exact-image/compiler receipts are +incomplete. Later captures are explicitly later. This is not current-main runtime qualification. +A duplicate fresh draft was stopped before compilation/timing. Retain future directory-specific +index designs separately and do not repeat this unchanged container policy until favorable. diff --git a/release-site.json b/release-site.json index d0c08b31eb..ce64044c1c 100644 --- a/release-site.json +++ b/release-site.json @@ -139,7 +139,8 @@ "docs/benchmarks/language-json-storage-20261107.md": "docs/benchmarks/language-json-storage-20261107.html", "docs/benchmarks/archive-ancestor-hint-20261107.md": "docs/benchmarks/archive-ancestor-hint-20261107.html", "docs/benchmarks/immutable-zip-locator-evaluation-20261107.md": "docs/benchmarks/immutable-zip-locator-evaluation-20261107.html", - "docs/benchmarks/queued-read-cancellation-20261107.md": "docs/benchmarks/queued-read-cancellation-20261107.html" + "docs/benchmarks/queued-read-cancellation-20261107.md": "docs/benchmarks/queued-read-cancellation-20261107.html", + "docs/benchmarks/archive-btree-historical-decision-20261107.md": "docs/benchmarks/archive-btree-historical-decision-20261107.html" }, "generated_html": { "XFF.md": { @@ -166,6 +167,7 @@ "docs/benchmarks/language-json-storage-20261107.json.gz": "docs/benchmarks/language-json-storage-20261107.json.gz", "docs/benchmarks/archive-ancestor-hint-20261107.json.gz": "docs/benchmarks/archive-ancestor-hint-20261107.json.gz", "docs/benchmarks/immutable-zip-locator-evaluation-20261107.json.gz": "docs/benchmarks/immutable-zip-locator-evaluation-20261107.json.gz", - "docs/benchmarks/queued-read-cancellation-20261107.json.gz": "docs/benchmarks/queued-read-cancellation-20261107.json.gz" + "docs/benchmarks/queued-read-cancellation-20261107.json.gz": "docs/benchmarks/queued-read-cancellation-20261107.json.gz", + "docs/benchmarks/archive-btree-historical-decision-20261107.json.gz": "docs/benchmarks/archive-btree-historical-decision-20261107.json.gz" } }