docs(bench): refresh baseline.txt with post-opt numbers and rationale
Captures Opt #1-#4 outcomes: - Opt #2 (get_unchecked) and #4 (timer-pop inline) landed. - Opt #3 (run_action proxy/context) was already free. - Opt #1 (Option<u16> → sentinel) tried, reverted (-0.6% net). Documents the gungraun-wrapper measurement floor.
This commit is contained in:
1 file changed
+149
-90
+149
-90
@@ -2,119 +2,163 @@
|
|||||||
#
|
#
|
||||||
# ─── Harness migration (iai-callgrind 0.16 → gungraun 0.18) ──────────
|
# ─── Harness migration (iai-callgrind 0.16 → gungraun 0.18) ──────────
|
||||||
#
|
#
|
||||||
# Switched from iai-callgrind to gungraun (a maintained fork). The
|
# Switched from iai-callgrind to gungraun (the renamed continuation —
|
||||||
# gungraun harness wraps each bench body with a slightly thicker shim
|
# crates.io shows iai-callgrind frozen at 0.16.1; the gh repo
|
||||||
# (extra `__gungraun_wrapper_id_mod_<id>::<id>` indirection, more
|
# iai-callgrind/iai-callgrind redirects to gungraun/gungraun). Same
|
||||||
# careful black_box / setup-result handoff), so absolute instruction
|
# valgrind backend, same callgrind output format. Old named baselines
|
||||||
# counts are NOT comparable to the iai-callgrind 0.16 numbers in git
|
# (pre_lca_fix, before_*) are gone; their deltas live in the historical
|
||||||
# history. The HSM dispatch work itself didn't change between the two
|
# block at the bottom for context.
|
||||||
# runs — the harness is just instrumenting more.
|
|
||||||
#
|
#
|
||||||
# Below are the gungraun_initial baselines. Old named baselines
|
# Re-run: cargo bench --bench dispatch
|
||||||
# (pre_lca_fix, before_path_precompute, …) are gone; their deltas live
|
# Diff: cargo bench --bench dispatch -- --baseline=gungraun_initial
|
||||||
# in the historical comment block at the bottom for context, but they
|
# Snapshot: cargo bench --bench dispatch -- --save-baseline=before_<change>
|
||||||
# are not directly comparable to current numbers.
|
|
||||||
#
|
|
||||||
# Re-run with:
|
|
||||||
#
|
|
||||||
# cargo bench --bench dispatch
|
|
||||||
#
|
|
||||||
# Save a named baseline for diffing:
|
|
||||||
#
|
|
||||||
# cargo bench --bench dispatch -- --save-baseline=before_<your_change>
|
|
||||||
# <change>
|
|
||||||
# cargo bench --bench dispatch -- --baseline=before_<your_change>
|
|
||||||
#
|
#
|
||||||
# Numbers below are from a 6.19.14-zen kernel x86_64 box, valgrind
|
# Numbers below are from a 6.19.14-zen kernel x86_64 box, valgrind
|
||||||
# 3.25.1, gungraun 0.18.2, gungraun-runner 0.18.2, --release.
|
# 3.25.1, gungraun 0.18.2, gungraun-runner 0.18.2, --release.
|
||||||
|
#
|
||||||
|
# Saved baselines (all under target/gungraun/...):
|
||||||
|
# gungraun_initial first gungraun-harness run, post-migration
|
||||||
|
# after_opt2_unchecked get_unchecked for codegen table indexing
|
||||||
|
# after_opt4_timer_inline pop_expired_nonempty inline (current numbers)
|
||||||
|
# before_more_opts alias for after_opt4_timer_inline (sweep base)
|
||||||
|
|
||||||
dispatch::dispatch::h2_cold_start cold:setup_h2_cold()
|
dispatch::dispatch::h2_cold_start cold:setup_h2_cold()
|
||||||
Instructions: 131
|
Instructions: 24
|
||||||
L1 Hits: 181
|
L1 Hits: 39
|
||||||
RAM Hits: 12
|
RAM Hits: 3
|
||||||
Estimated Cycles: 601
|
Estimated Cycles: 144
|
||||||
|
|
||||||
dispatch::dispatch::h2_warm_lateral warm:setup_h2_warm()
|
dispatch::dispatch::h2_warm_lateral warm:setup_h2_warm()
|
||||||
Instructions: 119
|
Instructions: 53
|
||||||
L1 Hits: 168
|
L1 Hits: 77
|
||||||
RAM Hits: 7
|
RAM Hits: 4
|
||||||
Estimated Cycles: 413
|
Estimated Cycles: 217
|
||||||
|
|
||||||
dispatch::dispatch::h4_cross_tree cross:setup_h4_warm()
|
dispatch::dispatch::h4_cross_tree cross:setup_h4_warm()
|
||||||
Instructions: 128
|
Instructions: 93
|
||||||
L1 Hits: 182
|
L1 Hits: 130
|
||||||
RAM Hits: 7
|
RAM Hits: 10
|
||||||
Estimated Cycles: 427
|
Estimated Cycles: 480
|
||||||
|
|
||||||
dispatch::dispatch::h4_up up:setup_h4_warm()
|
dispatch::dispatch::h4_up up:setup_h4_warm()
|
||||||
Instructions: 114
|
Instructions: 88
|
||||||
L1 Hits: 163
|
L1 Hits: 125
|
||||||
RAM Hits: 6
|
RAM Hits: 8
|
||||||
Estimated Cycles: 373
|
Estimated Cycles: 405
|
||||||
|
|
||||||
dispatch::dispatch::h4_self self_:setup_h4_warm()
|
dispatch::dispatch::h4_self self_:setup_h4_warm()
|
||||||
Instructions: 128
|
Instructions: 93
|
||||||
L1 Hits: 182
|
L1 Hits: 130
|
||||||
RAM Hits: 7
|
RAM Hits: 10
|
||||||
Estimated Cycles: 427
|
Estimated Cycles: 480
|
||||||
|
|
||||||
dispatch::dispatch::h8_cross_tree cross:setup_h8_warm()
|
dispatch::dispatch::h8_cross_tree cross:setup_h8_warm()
|
||||||
Instructions: 97
|
Instructions: 53
|
||||||
L1 Hits: 137
|
L1 Hits: 76
|
||||||
RAM Hits: 8
|
RAM Hits: 5
|
||||||
Estimated Cycles: 417
|
Estimated Cycles: 251
|
||||||
|
|
||||||
dispatch::dispatch::bubble_leaf leaf:setup_bubble_warm()
|
dispatch::dispatch::bubble_leaf leaf:setup_bubble_warm()
|
||||||
Instructions: 129
|
Instructions: 94
|
||||||
L1 Hits: 183
|
L1 Hits: 132
|
||||||
RAM Hits: 9
|
RAM Hits: 11
|
||||||
Estimated Cycles: 498
|
Estimated Cycles: 517
|
||||||
|
|
||||||
dispatch::dispatch::bubble_mid mid:setup_bubble_warm()
|
dispatch::dispatch::bubble_mid mid:setup_bubble_warm()
|
||||||
Instructions: 129
|
Instructions: 94
|
||||||
L1 Hits: 183
|
L1 Hits: 133
|
||||||
RAM Hits: 9
|
RAM Hits: 10
|
||||||
Estimated Cycles: 498
|
Estimated Cycles: 483
|
||||||
|
|
||||||
dispatch::dispatch::bubble_top top:setup_bubble_warm()
|
dispatch::dispatch::bubble_top top:setup_bubble_warm()
|
||||||
Instructions: 129
|
Instructions: 93
|
||||||
L1 Hits: 184
|
L1 Hits: 132
|
||||||
RAM Hits: 8
|
RAM Hits: 10
|
||||||
Estimated Cycles: 464
|
|
||||||
|
|
||||||
dispatch::dispatch::timer_fire_dispatch fire:setup_timer_fire_armed()
|
|
||||||
Instructions: 221
|
|
||||||
L1 Hits: 294
|
|
||||||
RAM Hits: 12
|
|
||||||
Estimated Cycles: 714
|
|
||||||
|
|
||||||
dispatch::dispatch::terminate_event quit:setup_term_warm()
|
|
||||||
Instructions: 68
|
|
||||||
L1 Hits: 99
|
|
||||||
RAM Hits: 7
|
|
||||||
Estimated Cycles: 344
|
|
||||||
|
|
||||||
dispatch::dispatch::bubble_cross_tree switch:setup_bubble_cross_warm()
|
|
||||||
Instructions: 120
|
|
||||||
L1 Hits: 167
|
|
||||||
RAM Hits: 9
|
|
||||||
Estimated Cycles: 482
|
Estimated Cycles: 482
|
||||||
|
|
||||||
dispatch::dispatch::chain_self_emit chain:setup_chain_warm()
|
dispatch::dispatch::timer_fire_dispatch fire:setup_timer_fire_armed()
|
||||||
Instructions: 500
|
Instructions: 175
|
||||||
L1 Hits: 689
|
L1 Hits: 237
|
||||||
RAM Hits: 18
|
RAM Hits: 7
|
||||||
Estimated Cycles: 1319
|
Estimated Cycles: 482
|
||||||
|
|
||||||
|
dispatch::dispatch::terminate_event quit:setup_term_warm()
|
||||||
|
Instructions: 53
|
||||||
|
L1 Hits: 81
|
||||||
|
RAM Hits: 5
|
||||||
|
Estimated Cycles: 256
|
||||||
|
|
||||||
|
dispatch::dispatch::bubble_cross_tree switch:setup_bubble_cross_warm()
|
||||||
|
Instructions: 85
|
||||||
|
L1 Hits: 116
|
||||||
|
RAM Hits: 11
|
||||||
|
Estimated Cycles: 501
|
||||||
|
|
||||||
|
dispatch::dispatch::chain_self_emit chain:setup_chain_warm()
|
||||||
|
Instructions: 439
|
||||||
|
L1 Hits: 613
|
||||||
|
RAM Hits: 17
|
||||||
|
Estimated Cycles: 1213
|
||||||
|
|
||||||
|
# ─── Migration deltas (gungraun_initial → current) ──────────────────
|
||||||
|
#
|
||||||
|
# Two optimization passes landed under gungraun:
|
||||||
|
#
|
||||||
|
# Opt #2: get_unchecked for codegen table indexing
|
||||||
|
# __DISPATCH, __DEFAULT_CHILD, __PATH_RANGE/_DATA, __TERMINATE_*
|
||||||
|
# all index by codegen-emitted state ids that are bounded by
|
||||||
|
# construction. Replacing the [idx] / [s..e] checked forms with
|
||||||
|
# get_unchecked unlocked aggressive inlining beyond what the
|
||||||
|
# bounds-check elision alone would buy: apply_path inlines into
|
||||||
|
# step() for h2/h4/h8/bubble; step() inlines into the bench
|
||||||
|
# wrapper for h2_cold_start / h2_warm_lateral / h8_cross_tree.
|
||||||
|
# The cold-path enter chain on h2_cold_start collapses to one
|
||||||
|
# u16 store. Bench impact:
|
||||||
|
# h2_cold_start 131 → 24 -82%
|
||||||
|
# h2_warm_lateral 119 → 53 -55%
|
||||||
|
# h8_cross_tree 97 → 53 -45%
|
||||||
|
# bubble_cross_tree 120 → 85 -29%
|
||||||
|
# h4_cross_tree/_self 128 → 93 -27%
|
||||||
|
# bubble_leaf/mid/top 129 → 94 -27%
|
||||||
|
# h4_up 114 → 88 -23%
|
||||||
|
# terminate_event 68 → 53 -22%
|
||||||
|
# chain_self_emit 500 → 439 -12%
|
||||||
|
# timer_fire_dispatch 221 → 212 -4%
|
||||||
|
#
|
||||||
|
# Opt #4: TimerTable::pop_expired_nonempty inline
|
||||||
|
# Drop #[cold], add #[inline]. The cold hint was forcing the body
|
||||||
|
# out-of-line at every step()-with-timers call site, paying full
|
||||||
|
# x86_64 ABI prologue/epilogue (~10-15 instr) per call. Inlining
|
||||||
|
# also let LLVM fold run_handlers into step() in the timer path
|
||||||
|
# (knock-on effect: the inliner wasn't budget-blocked by a separate
|
||||||
|
# pop_expired_nonempty call). Bench impact:
|
||||||
|
# timer_fire_dispatch 212 → 175 -17%
|
||||||
|
# (others unchanged)
|
||||||
|
#
|
||||||
|
# Tried but reverted:
|
||||||
|
#
|
||||||
|
# Opt #1: current: Option<u16> → u16 with u16::MAX sentinel.
|
||||||
|
# Theory: drop the discriminant byte, single-load is_none-check
|
||||||
|
# vs tag-then-value, one-write state-set. In practice net -9 instr
|
||||||
|
# across all 13 benches (-0.6%): real wins on terminate_event (-6)
|
||||||
|
# and bubble_cross_tree (-10) where the writes dominate, offset
|
||||||
|
# by compiler-inliner noise (+2 to +3 instr) on h4_cross_tree,
|
||||||
|
# h4_self, bubble_mid, bubble_top, chain_self_emit. Aggregate gain
|
||||||
|
# not worth the maintenance burden of sentinel-aware code at every
|
||||||
|
# self.current site plus an Option<u16> conversion helper for the
|
||||||
|
# observation API.
|
||||||
|
#
|
||||||
|
# Opt #3: hand-tighten run_action's EmitProxy/ActionContext init.
|
||||||
|
# Inspection of the compiled bench binary confirmed run_action,
|
||||||
|
# EmitProxy, ActionContext, and even the chain bench's emit_next
|
||||||
|
# vtable dispatch are all already fully inlined — no separate
|
||||||
|
# symbol exists for any of them. Already free; no change made.
|
||||||
|
#
|
||||||
# ─── Historical context (iai-callgrind era, NOT comparable) ─────────
|
# ─── Historical context (iai-callgrind era, NOT comparable) ─────────
|
||||||
#
|
#
|
||||||
# The codebase went through six dispatch-path optimization passes
|
# The codebase went through six dispatch-path optimization passes
|
||||||
# under iai-callgrind 0.16 before this harness migration. Cumulative
|
# under iai-callgrind 0.16 before the harness migration. Cumulative
|
||||||
# results vs. the original O(H²) LCA scan:
|
# h8_cross_tree result vs. the original O(H²) LCA scan: -82% instr.
|
||||||
#
|
|
||||||
# h8_cross_tree -82% instr / -72% cycles (deepest LCA stress)
|
|
||||||
# terminate_event -75% instr (combined) (precomputed exit table)
|
|
||||||
#
|
|
||||||
# The passes, in order:
|
# The passes, in order:
|
||||||
#
|
#
|
||||||
# 1. LCA contains() → O(H) ancestor-walk
|
# 1. LCA contains() → O(H) ancestor-walk
|
||||||
@@ -125,7 +169,8 @@ dispatch::dispatch::chain_self_emit chain:setup_chain_warm()
|
|||||||
# 6. Precomputed __TERMINATE_DATA + queue.clear() + u128→u64 nanoseconds
|
# 6. Precomputed __TERMINATE_DATA + queue.clear() + u128→u64 nanoseconds
|
||||||
#
|
#
|
||||||
# Pre-migration absolute numbers (iai-callgrind 0.16, for reference
|
# Pre-migration absolute numbers (iai-callgrind 0.16, for reference
|
||||||
# only; do not subtract from gungraun numbers):
|
# only; not directly comparable to gungraun numbers because the
|
||||||
|
# gungraun bench wrapper instruments slightly more):
|
||||||
#
|
#
|
||||||
# h2_cold_start 38 instr 201 cycles
|
# h2_cold_start 38 instr 201 cycles
|
||||||
# h2_warm_lateral 84 instr 397 cycles
|
# h2_warm_lateral 84 instr 397 cycles
|
||||||
@@ -139,7 +184,21 @@ dispatch::dispatch::chain_self_emit chain:setup_chain_warm()
|
|||||||
# bubble_cross_tree 94 instr 444 cycles
|
# bubble_cross_tree 94 instr 444 cycles
|
||||||
# chain_self_emit 419 instr 1301 cycles
|
# chain_self_emit 419 instr 1301 cycles
|
||||||
#
|
#
|
||||||
# Most of the absolute-instruction increase under gungraun is
|
# Bench coverage:
|
||||||
# harness, not HSM. Comparable iai-callgrind/gungraun deltas (e.g.,
|
#
|
||||||
# h8_cross_tree 93→97, terminate_event 69→68) confirm the dispatch
|
# `timer_fire_dispatch` — only bench that exercises pop_expired's
|
||||||
# work itself is unchanged.
|
# non-empty branch and `dispatch_trigger`.
|
||||||
|
# `terminate_event` — only bench that exercises `__check_terminate`'s
|
||||||
|
# true arm and `do_terminate`.
|
||||||
|
# `bubble_cross_tree` — handler on L1 (4 bubble hops) firing a
|
||||||
|
# 10-state cross-tree transition. Realistic HSM worst case.
|
||||||
|
#
|
||||||
|
# ─── Measurement floor ───────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# After Opt #2, the gungraun bench-harness wrapper is the dominant
|
||||||
|
# fixed cost on simple benches (~28-49 instr per bench). On
|
||||||
|
# h2_cold_start and h8_cross_tree, the entire bench body is inlined
|
||||||
|
# into the wrapper — at 24 and 53 instructions respectively, you are
|
||||||
|
# looking at the harness floor plus 0-25 instr of HSM work. Future
|
||||||
|
# delta tables should keep this in mind: a 5-instr "win" on h2 is
|
||||||
|
# 20% of the HSM-attributable cost, not 5/53 = 9%.
|
||||||
Reference in new issue
Block a user