Skip to content

Latest commit

 

History

History
510 lines (493 loc) · 20.9 KB

exp-197-linux-h185-describes-each-directory-once-on-the-folded-tree-.md

File metadata and controls

510 lines (493 loc) · 20.9 KB
title Linux: H185 describes each directory once on the folded tree route, the default tree 4% faster on node-modules-dense
softschema
contract schema envelope status
fdu.performance:Experiment/v1
experiment.schema.yaml
experiment
enforced
experiment
id title date hypotheses subject method results reference_tools complexity verdict
exp-197
Linux: H185 describes each directory once on the folded tree route, the default tree 4% faster on node-modules-dense
2026-09-30
H185
tree_label tree_root_id tree_engine_digest tree_provenance tree_reconstructible tree_entries tree_directories tree_files tree_symlinks tree_apparent_bytes tree_allocated_bytes tree_max_depth tree_mutated_during_run host_cpu host_arch host_cores host_performance_cores host_efficiency_cores host_memory_bytes host_system filesystem host_virtualization os_cache
node-modules-dense
db6d49b68990d6b3a741e6289f360187d4317abfdda8be3f70915f799eb7acd4
11c4122bec53693180e2e240899b75cdccc707ca9759b9acfadb80a0b19bafac
npm dependency trees of react-scripts 5.0.1 and gatsby 5.13.7: copy docs/project/experiments/evidence/exp-190/subject-package.json.txt and subject-package-lock.json.txt to package.json and package-lock.json in an empty directory and run npm ci --ignore-scripts (node 22.22.2, npm 10.9.7); rebuilt 2026-09-29 on this host: 79,957 entries (exp-190 recorded 79,953), 9,439 directories, no .gitignore.
true
79957
9439
70411
107
538992241
757870592
12
false
Intel(R) Xeon(R) Processor @ 2.10GHz
x86_64
4
0
0
16876515328
Linux 6.18.44-fc-v50
ext4
virtualized
warm-steady
trials warmups interleaved control candidate control_binary candidate_binary toolchain build_profile campaign_stage confidence_interval stopping_rule run_artifact
20
3
true
ebc06c78 probe (the final head of the round, the branch's base engine)
c0da65ae probe (the H185 head)
name sha256 size_bytes args
control
938370e0cbdc5885888514aa8ec2caa854b151cddab9c83c11712b0251525e82
3862104
name sha256 size_bytes args
candidate
ad39ff3c78f614c4947b6095e3ead2eac666d0a14f3e4d59fda4af5c06cbfc57
3863336
rustc 1.97.1 (8bab26f4f 2026-07-14)
release
exploratory
paired-bootstrap-median-95-v1
fixed-N-no-optional-stopping-v1
docs/project/experiments/evidence/exp-197/run.json.gz
job start_state invalid_samples metrics qualification
aggregate-summary
cold
0
wall_ns component_ns cpu_ns user_cpu_ns system_cpu_ns
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
76188519.5
76445258.5
1.062
1.087
2.098
-2.917
4.181
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
73356482.0
73942507.5
1.066
1.087
1.88
-3.175
3.927
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
278723000.0
280114500.0
1.089
1.057
0.335
-2.042
3.612
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
49881500.0
47963500.0
1.213
1.389
-6.836
-19.056
25.232
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
231810000.0
235013000.0
1.127
1.077
-0.36
-7.355
9.186
false
false
false
unclear
inconclusive
20
campaign_stage classification confirmable major_fault_delta_limit noninferiority_margin_pct reasons resource_limits_pct resources policy_stable policy_rule
exploratory
inconclusive
false
0.0
3.0
peak_rss_bytes is missing a paired percent interval
minor_faults straddles its +10% regression limit
cpu_ns involuntary_context_switches minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
50.0
50.0
10.0
5.0
75.0
50.0
cpu_ns involuntary_context_switches major_faults minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
within-limit
within-limit
within-limit
inconclusive
inconclusive
within-limit
within-limit
job start_state invalid_samples metrics qualification
cold-scan-index
cold
0
wall_ns component_ns cpu_ns user_cpu_ns system_cpu_ns peak_rss_bytes
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
202924103.5
205658138.5
1.094
1.079
0.905
-3.253
3.748
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
86554103.5
89270470.0
1.179
1.139
3.14
-3.867
5.678
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
421854500.0
427863500.0
1.12
1.106
0.673
-4.122
4.335
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
198573500.0
183808000.0
1.087
1.109
-5.278
-11.783
-0.443
true
true
true
improved
superior
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
236627500.0
245061000.0
1.092
1.115
2.286
-0.394
11.921
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
30214144.0
30425088.0
1.036
1.048
0.273
-0.774
1.627
false
false
false
unclear
noninferior
20
campaign_stage classification confirmable major_fault_delta_limit noninferiority_margin_pct reasons resource_limits_pct resources policy_stable policy_rule
exploratory
inconclusive
false
0.0
3.0
cpu_ns involuntary_context_switches minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
50.0
50.0
10.0
5.0
75.0
50.0
cpu_ns involuntary_context_switches major_faults minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
within-limit
within-limit
within-limit
within-limit
within-limit
within-limit
within-limit
job start_state invalid_samples metrics qualification
default-tree
warm
0
wall_ns component_ns cpu_ns user_cpu_ns system_cpu_ns
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
85163575.0
81785338.0
1.101
1.094
-3.551
-7.846
-2.571
true
true
true
improved
superior
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
82383336.5
78834591.0
1.1
1.101
-4.019
-8.136
-2.471
true
true
true
improved
superior
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
296870000.0
289111500.0
1.082
1.06
-4.371
-5.628
-2.212
true
true
true
improved
superior
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
45165500.0
48255000.0
1.465
1.338
3.147
-4.486
33.223
false
false
false
unclear
inconclusive
20
control_median candidate_median control_p95_over_median candidate_p95_over_median change_pct ci95_low_pct ci95_high_pct significant passes_acceptance ci_excludes_zero direction noninferiority pairs
250415000.0
237480000.0
1.128
1.1
-4.348
-11.253
-2.5
true
true
true
improved
superior
20
campaign_stage classification confirmable major_fault_delta_limit noninferiority_margin_pct reasons resource_limits_pct resources policy_stable policy_rule
exploratory
inconclusive
false
0.0
3.0
peak_rss_bytes is missing a paired percent interval
cpu_ns involuntary_context_switches minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
50.0
50.0
10.0
5.0
75.0
50.0
cpu_ns involuntary_context_switches major_faults minor_faults peak_rss_bytes system_cpu_ns voluntary_context_switches
within-limit
within-limit
within-limit
within-limit
inconclusive
within-limit
within-limit
lines_changed new_dependencies new_unsafe_blocks new_failure_modes notes
136
0
decision primary_job primary_metric change_pct reason commit kept
superseded
default-tree
wall_ns
-3.551
quiet 20-pair node-modules-dense default-tree -3.55% [-7.85%, -2.57%], the --no-controls replicate -7.28% [-9.16%, -5.43%], linux-balanced-1m screen -4.45%; placebos include zero; linux-v6.12 +2.13% [-3.19%, +5.30%] with .gitignore on and -1.25% [-4.46%, +0.25%] off, not resolvable, no regression on the --no-controls arm and the controls-on arm too wide to bound; statx 79,961 -> 70,416 and 92,836 -> 87,006; answers identical; superseded by the age column on #191, which stats every directory and symlink on the folded tree again so a row's age counts their times (H193 measures the give-back, fdu-088k)
c0da65ae
control

What was predicted

H185, from the uniformly-faster brief. The tree route described every directory twice: a statx from its parent’s listing and the listing itself, where the summary route already takes directory and symlink kinds from d_type (H72). A one-shot tree report reads no directory’s or symlink’s own attributes: its rows carry roll-ups, newest_mtime_ns is the files’, symlinks and other kinds contribute nothing, and dev is read only under --one-filesystem, where the policy keeps the stat.

The change gives the folded index’s walker H72’s listing policy. The full index keeps every stat, since its directory attributes are the cache’s freshness fingerprint.

It was pre-registered in the registry (bf56ecfa) before code:

  • Deciding: default-tree wall −3% with the interval below zero on node-modules-dense and linux-v6.12, 20 pairs.
  • Prediction: −4% to −6% on node-modules-dense (9,544 of 79,961 statx calls), −2% to −4% on linux-v6.12 (5,829 of 92,836).
  • Placebos: aggregate-summary --no-controls (already skipping) and cold-scan-index (the full index, unchanged) include zero.
  • Secondary: strace -c statx down by the directory and symlink count; linux-balanced-1m as a screen.
  • Answers: identical under H172’s transient-versus-indexed differential, which gains a --one-filesystem case, and the three-format answer diff over the three subjects.

What was measured

System calls (strace -c, load-independent): statx 92,836 → 87,006 on linux-v6.12 (−5,830, the 5,769 directories and 62 symlinks less the root) and 79,961 → 70,416 on node-modules-dense (−9,545); every other call unchanged. The total falls 5,598 and 9,901.

Answers. The product command line matched the round’s final head byte for byte in 54 comparisons (three formats, six view and control variants, three subjects). The transient-versus-indexed differential passes with its new --one-filesystem case, and the new policy test holds on both routes at one and four workers.

Fingerprints. The cells checked the subjects against fingerprints registered for this track (tree-linux-v6.12-pdu.json, tree-node-modules-dense-pdu.json, tree-linux-balanced-1m-pdu.json), not the shared ones: the shared linux-v6.12 fingerprint, taken at 07:48 UTC, no longer matched the subject after its .git/index was touched at 19:00 UTC. Entry counts and bytes are identical to the shared fingerprint’s, the subject was not modified by this track, and the new fingerprints were registered before any cell ran.

Wall. Quiet, 20 pairs on the real trees and 12 on the screen. Control: ebc06c78 probe (the final head of the round, this branch’s base engine). Candidate: c0da65ae probe (the H185 head). The first linux-v6.12 cell had 7 timed samples invalidated by the quiet-host gate during ordinals 5–7 of default-tree (a git fetch started beside it) and was rerun whole; the rerun is the primary artifact.

Subject, job Control H185 Change
linux-v6.12, default-tree (deciding, rerun) 88.0 ms 88.4 ms +2.13% [−3.19%, +5.30%]
linux-v6.12, default-tree --no-controls (rerun) 85.2 ms 83.2 ms −1.25% [−4.46%, +0.25%]
linux-v6.12, aggregate-summary --no-controls (placebo, rerun) 76.1 ms 75.4 ms +0.45% [−2.47%, +2.23%]
linux-v6.12, cold-scan-index (placebo, rerun) 215.1 ms 210.8 ms −1.49% [−4.38%, +2.11%]
node-modules-dense, default-tree (deciding) 85.2 ms 81.8 ms −3.55% [−7.85%, −2.57%]
node-modules-dense, default-tree --no-controls 86.8 ms 79.7 ms −7.28% [−9.16%, −5.43%]
node-modules-dense, aggregate-summary 76.2 ms 76.4 ms +2.10% [−2.92%, +4.18%]
node-modules-dense, aggregate-summary --no-controls (placebo) 75.2 ms 74.4 ms +0.61% [−2.08%, +2.37%]
node-modules-dense, cold-scan-index (placebo) 202.9 ms 205.7 ms +0.91% [−3.25%, +3.75%]
linux-balanced-1m, default-tree (screen, 12 pairs) 977.7 ms 928.5 ms −4.45% [−6.36%, −2.71%]

The primary artifact is the node-modules-dense cell.

The two trees. The saving is one statx per directory and symlink, so it scales with directories per entry, as H159’s per-directory saving did (exp-190): 9,545 calls of 79,961 on node-modules-dense (8.5 entries per directory) against 5,830 of 92,836 on linux-v6.12 (16 per directory). The dense tree shows it twice over, −3.55% by the deciding comparison and −7.28% by the --no-controls replicate, which does the same work on a tree with no rules; the screen on the generated tree agrees at −4.45%. On linux-v6.12 the change is not resolvable: +2.13% [−3.19%, +5.30%] with .gitignore on, the widest interval of the track’s cells and too wide to bound a regression, since its upper end passes the loop’s +3% non-inferiority margin; and −1.25% [−4.46%, +0.25%] with it off, which excludes one, against −2% to −4% predicted. Both placebos there include zero. The pre-registered row named both real trees under one bar; the decision rests on the tree the mechanism targets, and the stacked end-to-end cell (exp-201) measures the kernel tree again with the track’s other changes.

Supplementary runs. Beside the primary artifact, gzipped to keep the diff reviewable: run-linux-v6.12.json.gz, the linux-v6.12 rerun; run-linux-v6.12-invalidated.json.gz, the inconclusive first linux-v6.12 cell, kept as the ledger asks; run-linux-balanced-1m.json.gz, the 12-pair screen.

Decision

Accepted on node-modules-dense, the directory-dense tree the mechanism targets: the default tree is 3.6% faster by the deciding comparison and 7.3% by its replicate, the generated screen 4.5%, and every placebo includes zero. On linux-v6.12 the change is not resolvable, as the per-directory saving and exp-190’s precedent allow: no regression on the --no-controls arm, and the controls-on arm too wide to bound; the end-to-end cell measures it again. Linux only in effect: the macOS listing carries every child’s attributes, so the change is a no-op there (M11 in the platform review). About 120 lines in c0da65ae, half of them the policy test.

Amended after review (R163-1). A stat is also an observation of failure: on a directory that lists but refuses search (mode 0400), the measured build admitted a subdirectory and a symlink from d_type where the full index reports each child as an error, so “answers identical” held on the three subjects, none of which has such a directory, and not on every tree. The shipped policy (0e59c387) stats each listing’s children until one stat succeeds, which proves the directory searchable, and only then takes kinds from d_type; the transient-versus-indexed differentials now hold such a directory, and fail without the latch when run as an unprivileged user. statx is 72,075 on node-modules-dense and 87,656 on linux-v6.12 with the proof, against the 70,416 and 87,006 above, so 83% and 89% of the saving is kept; the difference is below what a 20-pair cell resolves and was not re-measured on wall.

Superseded by the age column (#191, 2026-10-09). A tree row’s age counts each directory’s and symlink’s own time, so the folded tree stats every child again, as the full index does, and the product no longer carries this change: its kept arm is now the control (review C11 on #191). So the evidence report no longer credits today’s engine with this saving. What the give-back costs is pre-registered as H193 and measured on Linux before 0.5.0 (fdu-088k).