From ec87025da6216e21083bf70f0e7cf5546df24c81 Mon Sep 17 00:00:00 2001 From: "Moe Charm (CI)" Date: Mon, 15 Dec 2025 11:28:40 +0900 Subject: [PATCH] =?UTF-8?q?Phase=2017=20v2=20(FORCE=5FLIBC=20fix)=20+=20Ph?= =?UTF-8?q?ase=2019-1b=20(FastLane=20Direct)=20=E2=80=94=20GO=20(+5.88%)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Phase 17 v2: FORCE_LIBC Gap Validation Fix **Critical bug fix**: Phase 17 v1 の測定が壊れていた **Problem**: HAKMEM_FORCE_LIBC_ALLOC=1 が FastLane より後でしか見えず、 same-binary A/B が実質 "hakmem vs hakmem" になっていた(+0.39% 誤測定) **Fix**: core/box/hak_wrappers.inc.h:171 と :645 に g_force_libc_alloc==1 の early bypass を追加、__libc_malloc/__libc_free に最初に直行 **Result**: 正しい同一バイナリ A/B 測定 - hakmem (FORCE_LIBC=0): 48.99M ops/s - libc (FORCE_LIBC=1): 79.72M ops/s (+62.7%) - system binary: 88.06M ops/s (+10.5% vs libc) **Gap 分解**: - Allocator 差: +62.7% (主戦場) - Layout penalty: +10.5% (副次的) **Conclusion**: Case A 確定 (allocator dominant, NOT layout) Phase 17 v1 の Case B 判定は誤り。 Files: - docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md (v2) - docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md (updated) --- ## Phase 19: FastLane Instruction Reduction Analysis **Goal**: libc との instruction gap (-35% instructions, -56% branches) を削減 **perf stat 分析** (FORCE_LIBC=0 vs 1, 200M ops): - hakmem: 209.09 instructions/op, 52.33 branches/op - libc: 135.92 instructions/op, 22.93 branches/op - Delta: +73.17 instructions/op (+53.8%), +29.40 branches/op (+128.2%) **Hot path** (perf report): - front_fastlane_try_free: 23.97% cycles - malloc wrapper: 23.84% cycles - free wrapper: 6.82% cycles - **Wrapper overhead: ~55% of all cycles** **Reduction candidates**: - A: Wrapper layer 削除 (-17.5 inst/op, +10-15% 期待) - B: ENV snapshot 統合 (-10.0 inst/op, +5-8%) - C: Stats 削除 (-5.0 inst/op, +3-5%) - D: Header inline (-4.0 inst/op, +2-3%) - E: Route fast path (-3.5 inst/op, +2-3%) Files: - docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md - docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md --- ## Phase 19-1b: FastLane Direct — GO (+5.88%) **Strategy**: Wrapper layer を bypass し、core allocator を直接呼ぶ - free() → free_tiny_fast() (not free_tiny_fast_hot) - malloc() → malloc_tiny_fast() **Phase 19-1 が NO-GO (-3.81%) だった原因**: 1. __builtin_expect(fastlane_direct_enabled(), 0) が逆効果(A/B 不公平) 2. free_tiny_fast_hot() が誤選択(free_tiny_fast() が勝ち筋) **Phase 19-1b の修正**: 1. __builtin_expect() 削除 2. free_tiny_fast() を直接呼び出し **Result** (Mixed, 10-run, 20M iters, ws=400): - Baseline (FASTLANE_DIRECT=0): 49.17M ops/s - Optimized (FASTLANE_DIRECT=1): 52.06M ops/s - **Delta: +5.88%** (GO 基準 +5% クリア) **perf stat** (200M iters): - Instructions/op: 199.90 → 169.45 (-30.45, -15.23%) - Branches/op: 51.49 → 41.52 (-9.97, -19.36%) - Cycles/op: 88.88 → 84.37 (-4.51, -5.07%) - I-cache miss: 111K → 98K (-11.79%) **Trade-offs** (acceptable): - iTLB miss: +41.46% (front-end cost) - dTLB miss: +29.15% (backend cost) - Overall gain (+5.88%) outweighs costs **Implementation**: 1. **ENV gate**: core/box/fastlane_direct_env_box.{h,c} - HAKMEM_FASTLANE_DIRECT=0/1 (default: 0, opt-in) - Single _Atomic global (wrapper キャッシュ問題を解決) 2. **Wrapper 修正**: core/box/hak_wrappers.inc.h - malloc: direct call to malloc_tiny_fast() when FASTLANE_DIRECT=1 - free: direct call to free_tiny_fast() when FASTLANE_DIRECT=1 - Safety: !g_initialized では direct 使わない、fallback 維持 3. **Preset 昇格**: core/bench_profile.h:88 - bench_setenv_default("HAKMEM_FASTLANE_DIRECT", "1") - Comment: +5.88% proven on Mixed, 10-run 4. **cleanenv 更新**: scripts/run_mixed_10_cleanenv.sh:22 - HAKMEM_FASTLANE_DIRECT=${HAKMEM_FASTLANE_DIRECT:-1} - Phase 9/10 と同様に昇格 **Verdict**: GO — 本線採用、プリセット昇格完了 **Rollback**: HAKMEM_FASTLANE_DIRECT=0 で既存 FastLane path に戻る Files: - core/box/fastlane_direct_env_box.{h,c} (new) - core/box/hak_wrappers.inc.h (modified) - core/bench_profile.h (preset promotion) - scripts/run_mixed_10_cleanenv.sh (ENV default aligned) - Makefile (new obj) - docs/analysis/PHASE19_1B_FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md --- ## Cumulative Performance - Baseline (all optimizations OFF): ~40M ops/s (estimated) - Current (Phase 19-1b): 52.06M ops/s - **Cumulative gain: ~+30% from baseline** Remaining gap to libc (79.72M): - Current: 52.06M ops/s - Target: 79.72M ops/s - **Gap: +53.2%** (was +62.7% before Phase 19-1b) Next: Phase 19-2 (ENV snapshot consolidation, +5-8% expected) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- CURRENT_TASK.md | 118 +++- Makefile | 8 +- core/bench_profile.h | 7 + core/box/fastlane_direct_env_box.c | 15 + core/box/fastlane_direct_env_box.h | 46 ++ core/box/hak_wrappers.inc.h | 63 ++ ...E_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md | 88 ++- ...LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md | 8 +- ...FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md | 307 ++++++++++ ...FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md | 543 ++++++++++++++++++ ...STRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md | 64 +++ hakmem.d | 4 +- perf.data.phase19_hakmem | Bin 0 -> 352048 bytes scripts/run_mixed_10_cleanenv.sh | 2 + 14 files changed, 1213 insertions(+), 60 deletions(-) create mode 100644 core/box/fastlane_direct_env_box.c create mode 100644 core/box/fastlane_direct_env_box.h create mode 100644 docs/analysis/PHASE19_1B_FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md create mode 100644 docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md create mode 100644 docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md create mode 100644 perf.data.phase19_hakmem diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index f0683415..26a3678b 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -1,5 +1,117 @@ # 本線タスク(現在) +## 更新メモ(2025-12-15 Phase 19-1b FASTLANE-DIRECT-1B) + +### Phase 19-1b FASTLANE-DIRECT-1B: FastLane Direct (Revised) — ✅ GO (+5.88%) + +**Result**: Phase 19-1 の修正版が成功。__builtin_expect() 削除 + free_tiny_fast() 直呼び で throughput **+5.88%** 達成。 + +**A/B Test Results**: +- Baseline: 49.17M ops/s (FASTLANE_DIRECT=0) +- Optimized: 52.06M ops/s (FASTLANE_DIRECT=1) +- Delta: **+5.88%** (GO判定、+5%目標クリア) + +**perf stat Analysis** (200M ops): +- Instructions: **-15.23%** (199.90 → 169.45/op, -30.45 削減) +- Branches: **-19.36%** (51.49 → 41.52/op, -9.97 削減) +- Cycles: **-5.07%** (88.88 → 84.37/op) +- I-cache misses: -11.79% (Good) +- iTLB misses: +41.46% (Bad, but overall gain wins) +- dTLB misses: +29.15% (Bad, but overall gain wins) + +**犯人特定**: +1. Phase 19-1 の NO-GO 原因: `__builtin_expect(fastlane_direct_enabled(), 0)` が逆効果 +2. `free_tiny_fast_hot()` より `free_tiny_fast()` が勝ち筋(unified cache の winner) +3. 修正により wrapper overhead 削減 → instruction/branch の大幅削減 + +**修正内容**: +- File: `/mnt/workdisk/public_share/hakmem/core/box/hak_wrappers.inc.h` +- malloc: `__builtin_expect(fastlane_direct_enabled(), 0)` → `fastlane_direct_enabled()` +- free: `free_tiny_fast_hot()` → `free_tiny_fast()` (勝ち筋に変更) +- Safety: `!g_initialized` では direct を使わず既存経路へフォールバック(FastLane と同じ fail-fast) +- Safety: malloc miss は `malloc_cold()` を直呼びせず既存 wrapper 経路へ落とす(lock_depth 前提を守る) +- ENV cache: `fastlane_direct_env_refresh_from_env()` が wrapper と同一の `_Atomic` に反映されるように単一グローバル化 + +**Next**: Phase 19-1b は本線採用。ENV: `HAKMEM_FASTLANE_DIRECT=1` で運用。 + +--- + +## 前回タスク(Phase 19 FASTLANE-INSTRUCTION-REDUCTION-1) + +### Phase 19 FASTLANE-INSTRUCTION-REDUCTION-1: FastLane Instruction Reduction v1 — 📊 ANALYSIS COMPLETE + +結果: perf stat/record 分析により、**libc との gap の本質**を特定。設計ドキュメント完成。 + +- 設計: `docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md` +- perf データ: 保存済み(perf_stat_hakmem.txt, perf_stat_libc.txt, perf.data.phase19_hakmem) + +### Gap Analysis(200M ops baseline) + +**Per-operation overhead** (hakmem vs libc): +- Instructions/op: **209.09 vs 135.92** (+73.17, **+53.8%**) +- Branches/op: **52.33 vs 22.93** (+29.40, **+128.2%**) +- Cycles/op: **96.48 vs 54.69** (+41.79, +76.4%) +- Throughput: **44.88M vs 77.62M ops/s** (+73.0% gap) + +**Critical finding**: hakmem は **73 extra instructions** と **29 extra branches** per-op を実行。これが throughput gap の全原因。 + +### Hot Path Breakdown(perf report) + +Top wrapper overhead (合計 ~55% of cycles): +- `front_fastlane_try_free`: **23.97%** +- `malloc`: **23.84%** +- `free`: **6.82%** + +Wrapper layer が cycles の過半を消費(二重検証、ENV checks、class mask checks など)。 + +### Reduction Candidates(優先度順) + +1. **Candidate A: FastLane Wrapper Layer 削除** (highest ROI) + - Impact: **-17.5 instructions/op, -6.0 branches/op** (+10-15% throughput) + - Risk: **LOW**(free_tiny_fast_hot 既存) + - 理由: 二重 header validation + ENV checks 排除 + +2. **Candidate B: ENV Snapshot 統合** (high ROI) + - Impact: **-10.0 instructions/op, -4.0 branches/op** (+5-8% throughput) + - Risk: **MEDIUM**(ENV invalidation 対応必要) + - 理由: 3+ 回の ENV check を 1 回に統合 + +3. **Candidate C: Stats Counters 削除** (medium ROI) + - Impact: **-5.0 instructions/op, -2.5 branches/op** (+3-5% throughput) + - Risk: **LOW**(compile-time optional) + - 理由: Atomic increment overhead 排除 + +4. **Candidate D: Header Validation Inline** (medium ROI) + - Impact: **-4.0 instructions/op, -1.5 branches/op** (+2-3% throughput) + - Risk: **MEDIUM**(caller 検証前提) + - 理由: 二重 header load 排除 + +5. **Candidate E: Static Route Fast Path** (lower ROI) + - Impact: **-3.5 instructions/op, -1.5 branches/op** (+2-3% throughput) + - Risk: **LOW**(route table static) + - 理由: Function call を bit test に置換 + +**Combined estimate** (80% efficiency): +- Instructions/op: 209.09 → **177.09** (gap: +53.8% → +30.3%) +- Branches/op: 52.33 → **39.93** (gap: +128.2% → +74.1%) +- Throughput: 44.88M → **54.3M ops/s** (+21%, **目標 +15-25% 超過達成**) + +### Implementation Plan + +- **Phase 19-1** (P0): FastLane Wrapper 削除 (2-3h, +10-15%) +- **Phase 19-2** (P1): ENV Snapshot 統合 (4-6h, +5-8%) +- **Phase 19-3** (P2): Stats + Header Inline (2-3h, +3-5%) +- **Phase 19-4** (P3): Route Fast Path (2-3h, +2-3%) + +### 次の手順 + +1. Phase 19-1 実装開始(FastLane layer 削除、直接 free_tiny_fast_hot 呼び出し) +2. perf stat で instruction/branch reduction 検証 +3. Mixed 10-run で throughput improvement 測定 +4. Phase 19-2-4 を順次実装 + +--- + ## 更新メモ(2025-12-15 Phase 18 HOT-TEXT-ISOLATION-1) ### Phase 18 HOT-TEXT-ISOLATION-1: Hot Text Isolation v1 — ❌ NO-GO / FROZEN @@ -17,9 +129,9 @@ - Hot/cold 属性が実際には適用されていない(実装の不完全性) 重要な知見: -- Phase 17 の結論を再確認: bottleneck は **instruction count** と **memory latency** -- Code layout 最適化では 2.30 IPC の壁を越えられない -- 次の一手: instruction count を直接削る Phase 18 v2 (BENCH_MINIMAL) へ +- Phase 17 v2(FORCE_LIBC 修正後): same-binary A/B で **libc が +62.7%**(≒1.63×)速い → gap の主因は **allocator work**(layout alone ではない) +- ただし `bench_random_mixed_system` は `libc-in-hakmem-binary` よりさらに **+10.5%** 速い → wrapper/text 環境の penalty も残る +- Phase 18 v2(BENCH_MINIMAL)は「足し算の固定費」を削る方向として有効だが、-5% instructions 程度では +62% gap を埋められない ## 更新メモ(2025-12-14 Phase 6 FRONT-FASTLANE-1) diff --git a/Makefile b/Makefile index 7a7bd157..2613a985 100644 --- a/Makefile +++ b/Makefile @@ -253,12 +253,12 @@ LDFLAGS += $(EXTRA_LDFLAGS) # Targets TARGET = test_hakmem -OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/free_publish_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/ss_pt_impl.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/hakmem_env_snapshot_box.o core/box/tiny_c7_preserve_header_env_box.o core/box/tiny_tcache_env_box.o core/box/tiny_unified_lifo_env_box.o core/box/front_fastlane_alloc_legacy_direct_env_box.o core/page_arena.o core/front/tiny_unified_cache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o +OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/free_publish_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/ss_pt_impl.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/hakmem_env_snapshot_box.o core/box/tiny_c7_preserve_header_env_box.o core/box/tiny_tcache_env_box.o core/box/tiny_unified_lifo_env_box.o core/box/front_fastlane_alloc_legacy_direct_env_box.o core/box/fastlane_direct_env_box.o core/page_arena.o core/front/tiny_unified_cache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o OBJS = $(OBJS_BASE) # Shared library SHARED_LIB = libhakmem.so -SHARED_OBJS = hakmem_shared.o hakmem_config_shared.o hakmem_tiny_config_shared.o hakmem_ucb1_shared.o hakmem_bigcache_shared.o hakmem_pool_shared.o hakmem_l25_pool_shared.o hakmem_site_rules_shared.o hakmem_tiny_shared.o core/box/ss_allocation_box_shared.o superslab_stats_shared.o superslab_cache_shared.o superslab_ace_shared.o superslab_slab_shared.o superslab_backend_shared.o core/superslab_head_stub_shared.o hakmem_smallmid_shared.o core/box/superslab_expansion_box_shared.o core/box/integrity_box_shared.o core/box/mailbox_box_shared.o core/box/front_gate_box_shared.o core/box/front_gate_classifier_shared.o core/box/free_publish_box_shared.o core/box/capacity_box_shared.o core/box/carve_push_box_shared.o core/box/prewarm_box_shared.o core/box/ss_hot_prewarm_box_shared.o core/box/front_metrics_box_shared.o core/box/bench_fast_box_shared.o core/box/ss_addr_map_box_shared.o core/box/ss_pt_impl_shared.o core/box/slab_recycling_box_shared.o core/box/pagefault_telemetry_box_shared.o core/box/tiny_sizeclass_hist_box_shared.o core/box/tiny_env_box_shared.o core/box/tiny_route_box_shared.o core/box/free_front_v3_env_box_shared.o core/box/free_path_stats_box_shared.o core/box/free_dispatch_stats_box_shared.o core/box/alloc_gate_stats_box_shared.o core/box/tiny_page_box_shared.o core/box/tiny_class_policy_box_shared.o core/box/tiny_class_stats_box_shared.o core/box/tiny_policy_learner_box_shared.o core/box/ss_budget_box_shared.o core/box/tiny_mem_stats_box_shared.o core/box/wrapper_env_box_shared.o core/box/free_wrapper_env_snapshot_box_shared.o core/box/malloc_wrapper_env_snapshot_box_shared.o core/box/madvise_guard_box_shared.o core/box/libm_reloc_guard_box_shared.o core/box/hakmem_env_snapshot_box_shared.o core/box/tiny_c7_preserve_header_env_box_shared.o core/box/tiny_tcache_env_box_shared.o core/box/tiny_unified_lifo_env_box_shared.o core/box/front_fastlane_alloc_legacy_direct_env_box_shared.o core/page_arena_shared.o core/front/tiny_unified_cache_shared.o core/tiny_alloc_fast_push_shared.o core/tiny_c7_ultra_segment_shared.o core/tiny_c7_ultra_shared.o core/link_stubs_shared.o core/tiny_failfast_shared.o tiny_sticky_shared.o tiny_remote_shared.o tiny_publish_shared.o tiny_debug_ring_shared.o hakmem_tiny_magazine_shared.o hakmem_tiny_stats_shared.o hakmem_tiny_sfc_shared.o hakmem_tiny_query_shared.o hakmem_tiny_rss_shared.o hakmem_tiny_registry_shared.o hakmem_tiny_remote_target_shared.o hakmem_tiny_bg_spill_shared.o tiny_adaptive_sizing_shared.o hakmem_super_registry_shared.o hakmem_shared_pool_shared.o hakmem_shared_pool_acquire_shared.o hakmem_shared_pool_release_shared.o hakmem_elo_shared.o hakmem_batch_shared.o hakmem_p2_shared.o hakmem_sizeclass_dist_shared.o hakmem_evo_shared.o hakmem_debug_shared.o hakmem_sys_shared.o hakmem_whale_shared.o hakmem_policy_shared.o hakmem_ace_shared.o hakmem_ace_stats_shared.o hakmem_ace_controller_shared.o hakmem_ace_metrics_shared.o hakmem_ace_ucb1_shared.o hakmem_prof_shared.o hakmem_learner_shared.o hakmem_size_hist_shared.o hakmem_learn_log_shared.o hakmem_syscall_shared.o tiny_fastcache_shared.o core/box/super_reg_box_shared.o core/box/shared_pool_box_shared.o core/box/remote_side_box_shared.o core/tiny_destructors_shared.o +SHARED_OBJS = hakmem_shared.o hakmem_config_shared.o hakmem_tiny_config_shared.o hakmem_ucb1_shared.o hakmem_bigcache_shared.o hakmem_pool_shared.o hakmem_l25_pool_shared.o hakmem_site_rules_shared.o hakmem_tiny_shared.o core/box/ss_allocation_box_shared.o superslab_stats_shared.o superslab_cache_shared.o superslab_ace_shared.o superslab_slab_shared.o superslab_backend_shared.o core/superslab_head_stub_shared.o hakmem_smallmid_shared.o core/box/superslab_expansion_box_shared.o core/box/integrity_box_shared.o core/box/mailbox_box_shared.o core/box/front_gate_box_shared.o core/box/front_gate_classifier_shared.o core/box/free_publish_box_shared.o core/box/capacity_box_shared.o core/box/carve_push_box_shared.o core/box/prewarm_box_shared.o core/box/ss_hot_prewarm_box_shared.o core/box/front_metrics_box_shared.o core/box/bench_fast_box_shared.o core/box/ss_addr_map_box_shared.o core/box/ss_pt_impl_shared.o core/box/slab_recycling_box_shared.o core/box/pagefault_telemetry_box_shared.o core/box/tiny_sizeclass_hist_box_shared.o core/box/tiny_env_box_shared.o core/box/tiny_route_box_shared.o core/box/free_front_v3_env_box_shared.o core/box/free_path_stats_box_shared.o core/box/free_dispatch_stats_box_shared.o core/box/alloc_gate_stats_box_shared.o core/box/tiny_page_box_shared.o core/box/tiny_class_policy_box_shared.o core/box/tiny_class_stats_box_shared.o core/box/tiny_policy_learner_box_shared.o core/box/ss_budget_box_shared.o core/box/tiny_mem_stats_box_shared.o core/box/wrapper_env_box_shared.o core/box/free_wrapper_env_snapshot_box_shared.o core/box/malloc_wrapper_env_snapshot_box_shared.o core/box/madvise_guard_box_shared.o core/box/libm_reloc_guard_box_shared.o core/box/hakmem_env_snapshot_box_shared.o core/box/tiny_c7_preserve_header_env_box_shared.o core/box/tiny_tcache_env_box_shared.o core/box/tiny_unified_lifo_env_box_shared.o core/box/front_fastlane_alloc_legacy_direct_env_box_shared.o core/box/fastlane_direct_env_box_shared.o core/page_arena_shared.o core/front/tiny_unified_cache_shared.o core/tiny_alloc_fast_push_shared.o core/tiny_c7_ultra_segment_shared.o core/tiny_c7_ultra_shared.o core/link_stubs_shared.o core/tiny_failfast_shared.o tiny_sticky_shared.o tiny_remote_shared.o tiny_publish_shared.o tiny_debug_ring_shared.o hakmem_tiny_magazine_shared.o hakmem_tiny_stats_shared.o hakmem_tiny_sfc_shared.o hakmem_tiny_query_shared.o hakmem_tiny_rss_shared.o hakmem_tiny_registry_shared.o hakmem_tiny_remote_target_shared.o hakmem_tiny_bg_spill_shared.o tiny_adaptive_sizing_shared.o hakmem_super_registry_shared.o hakmem_shared_pool_shared.o hakmem_shared_pool_acquire_shared.o hakmem_shared_pool_release_shared.o hakmem_elo_shared.o hakmem_batch_shared.o hakmem_p2_shared.o hakmem_sizeclass_dist_shared.o hakmem_evo_shared.o hakmem_debug_shared.o hakmem_sys_shared.o hakmem_whale_shared.o hakmem_policy_shared.o hakmem_ace_shared.o hakmem_ace_stats_shared.o hakmem_ace_controller_shared.o hakmem_ace_metrics_shared.o hakmem_ace_ucb1_shared.o hakmem_prof_shared.o hakmem_learner_shared.o hakmem_size_hist_shared.o hakmem_learn_log_shared.o hakmem_syscall_shared.o tiny_fastcache_shared.o core/box/super_reg_box_shared.o core/box/shared_pool_box_shared.o core/box/remote_side_box_shared.o core/tiny_destructors_shared.o # Pool TLS Phase 1 (enable with POOL_TLS_PHASE1=1) ifeq ($(POOL_TLS_PHASE1),1) @@ -285,7 +285,7 @@ endif # Benchmark targets BENCH_HAKMEM = bench_allocators_hakmem BENCH_SYSTEM = bench_allocators_system -BENCH_HAKMEM_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/free_publish_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/tiny_free_route_cache_env_box.o core/page_arena.o core/front/tiny_unified_cache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o bench_allocators_hakmem.o +BENCH_HAKMEM_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/free_publish_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/tiny_free_route_cache_env_box.o core/box/fastlane_direct_env_box.o core/page_arena.o core/front/tiny_unified_cache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o bench_allocators_hakmem.o BENCH_HAKMEM_OBJS = $(BENCH_HAKMEM_OBJS_BASE) ifeq ($(POOL_TLS_PHASE1),1) BENCH_HAKMEM_OBJS += pool_tls.o pool_refill.o pool_tls_arena.o pool_tls_registry.o pool_tls_remote.o @@ -462,7 +462,7 @@ test-box-refactor: box-refactor ./larson_hakmem 10 8 128 1024 1 12345 4 # Phase 4: Tiny Pool benchmarks (properly linked with hakmem) -TINY_BENCH_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/free_publish_box.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/ss_pt_impl.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/tiny_free_route_cache_env_box.o core/box/hakmem_env_snapshot_box.o core/box/tiny_c7_preserve_header_env_box.o core/box/tiny_tcache_env_box.o core/box/tiny_unified_lifo_env_box.o core/box/front_fastlane_alloc_legacy_direct_env_box.o core/page_arena.o core/front/tiny_unified_cache.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o +TINY_BENCH_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/free_publish_box.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/ss_pt_impl.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/free_front_v3_env_box.o core/box/free_path_stats_box.o core/box/free_dispatch_stats_box.o core/box/free_cold_shape_env_box.o core/box/free_cold_shape_stats_box.o core/box/alloc_gate_stats_box.o core/box/tiny_c6_ultra_free_box.o core/box/tiny_c5_ultra_free_box.o core/box/tiny_c4_ultra_free_box.o core/box/tiny_ultra_tls_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/tiny_static_route_box.o core/box/tiny_metadata_cache_hot_box.o core/box/wrapper_env_box.o core/box/free_wrapper_env_snapshot_box.o core/box/malloc_wrapper_env_snapshot_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/box/tiny_free_route_cache_env_box.o core/box/hakmem_env_snapshot_box.o core/box/tiny_c7_preserve_header_env_box.o core/box/tiny_tcache_env_box.o core/box/tiny_unified_lifo_env_box.o core/box/front_fastlane_alloc_legacy_direct_env_box.o core/box/fastlane_direct_env_box.o core/page_arena.o core/front/tiny_unified_cache.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/tiny_alloc_fast_push.o core/tiny_c7_ultra_segment.o core/tiny_c7_ultra.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o core/smallobject_hotbox_v5.o core/smallsegment_v5.o core/smallobject_cold_iface_v5.o core/smallsegment_v6.o core/smallobject_cold_iface_v6.o core/smallobject_core_v6.o core/region_id_v6.o core/smallsegment_v7.o core/smallobject_cold_iface_v7.o core/mid_hotbox_v3.o core/smallobject_policy_v7.o core/smallobject_segment_mid_v3.o core/smallobject_cold_iface_mid_v3.o core/smallobject_stats_mid_v3.o core/smallobject_learner_v2.o core/smallobject_mid_v35.o TINY_BENCH_OBJS = $(TINY_BENCH_OBJS_BASE) ifeq ($(POOL_TLS_PHASE1),1) TINY_BENCH_OBJS += pool_tls.o pool_refill.o core/pool_tls_arena.o pool_tls_registry.o pool_tls_remote.o diff --git a/core/bench_profile.h b/core/bench_profile.h index 4fc28986..501e735e 100644 --- a/core/bench_profile.h +++ b/core/bench_profile.h @@ -14,6 +14,7 @@ #include "box/tiny_tcache_env_box.h" // tiny_tcache_env_refresh_from_env (Phase 14 v1) #include "box/tiny_unified_lifo_env_box.h" // tiny_unified_lifo_env_refresh_from_env (Phase 15 v1) #include "box/front_fastlane_alloc_legacy_direct_env_box.h" // front_fastlane_alloc_legacy_direct_env_refresh_from_env (Phase 16 v1) +#include "box/fastlane_direct_env_box.h" // fastlane_direct_env_refresh_from_env (Phase 19-1) #endif // env が未設定のときだけ既定値を入れる @@ -84,6 +85,8 @@ static inline void bench_apply_profile(void) { bench_setenv_default("HAKMEM_FRONT_FASTLANE", "1"); // Phase 6-2: Front FastLane Free DeDup (+5.18% proven on Mixed, 10-run) bench_setenv_default("HAKMEM_FRONT_FASTLANE_FREE_DEDUP", "1"); + // Phase 19-1b: FastLane Direct (wrapper layer bypass, +5.88% proven on Mixed, 10-run) + bench_setenv_default("HAKMEM_FASTLANE_DIRECT", "1"); // Phase 9: FREE-TINY-FAST MONO DUALHOT (+2.72% proven on Mixed, 10-run) bench_setenv_default("HAKMEM_FREE_TINY_FAST_MONO_DUALHOT", "1"); // Phase 10: FREE-TINY-FAST MONO LEGACY DIRECT (+1.89% proven on Mixed, 10-run) @@ -119,6 +122,8 @@ static inline void bench_apply_profile(void) { bench_setenv_default("HAKMEM_FRONT_FASTLANE", "1"); // Phase 6-2: Front FastLane Free DeDup (+5.18% proven on Mixed, 10-run) bench_setenv_default("HAKMEM_FRONT_FASTLANE_FREE_DEDUP", "1"); + // Phase 19-1b: FastLane Direct (wrapper layer bypass) + bench_setenv_default("HAKMEM_FASTLANE_DIRECT", "1"); // Phase 2 B3: Routing branch shape optimization (LIKELY on LEGACY, cold helper for rare routes) bench_setenv_default("HAKMEM_TINY_ALLOC_ROUTE_SHAPE", "1"); } else if (strcmp(p, "C6_V7_STUB") == 0) { @@ -196,5 +201,7 @@ static inline void bench_apply_profile(void) { tiny_unified_lifo_env_refresh_from_env(); // Phase 16 v1: Sync LEGACY direct ENV cache after bench_profile putenv defaults. front_fastlane_alloc_legacy_direct_env_refresh_from_env(); + // Phase 19-1: Sync FastLane Direct ENV cache after bench_profile putenv defaults. + fastlane_direct_env_refresh_from_env(); #endif } diff --git a/core/box/fastlane_direct_env_box.c b/core/box/fastlane_direct_env_box.c new file mode 100644 index 00000000..572ea2cc --- /dev/null +++ b/core/box/fastlane_direct_env_box.c @@ -0,0 +1,15 @@ +// fastlane_direct_env_box.c - Phase 19-1: FastLane Direct Path ENV Control (implementation) + +#include "fastlane_direct_env_box.h" +#include +#include + +_Atomic int g_fastlane_direct_enabled = -1; + +// Refresh cached ENV flag from environment variable +// Called during benchmark ENV reloads to pick up runtime changes +void fastlane_direct_env_refresh_from_env(void) { + const char* e = getenv("HAKMEM_FASTLANE_DIRECT"); + int enable = (e && *e && *e != '0') ? 1 : 0; + atomic_store_explicit(&g_fastlane_direct_enabled, enable, memory_order_relaxed); +} diff --git a/core/box/fastlane_direct_env_box.h b/core/box/fastlane_direct_env_box.h new file mode 100644 index 00000000..b6ff1f73 --- /dev/null +++ b/core/box/fastlane_direct_env_box.h @@ -0,0 +1,46 @@ +// fastlane_direct_env_box.h - Phase 19-1: FastLane Direct Path ENV Control +// +// Goal: Remove wrapper layer overhead (30.79% of cycles) by calling core allocator directly +// Strategy: Compile-time + runtime gate to bypass front_fastlane_try_*() wrapper +// +// Box Theory: +// - Boundary: HAKMEM_FASTLANE_DIRECT=0/1 (default: 0, opt-in) +// - Rollback: ENV=0 reverts to existing FastLane wrapper path +// - Observability: perf stat shows instruction/branch reduction +// +// Expected Performance: +// - Reduction: -17.5 instructions/op, -6.0 branches/op +// - Impact: +10-15% throughput (remove 30% wrapper overhead) +// +// ENV Variables: +// HAKMEM_FASTLANE_DIRECT=0/1 # Enable direct path (default: 0, research box) + +#pragma once + +#include +#include + +// ENV control: cached flag for fastlane_direct_enabled() +// -1: uninitialized, 0: disabled, 1: enabled +// NOTE: Must be a single global (not header-static) so bench_profile refresh can +// update the same cache used by malloc/free wrappers. +extern _Atomic int g_fastlane_direct_enabled; + +// Runtime check: Is FastLane Direct path enabled? +// Returns: 1 if enabled, 0 if disabled +// Hot path: Single atomic load (after first call) +static inline int fastlane_direct_enabled(void) { + int val = atomic_load_explicit(&g_fastlane_direct_enabled, memory_order_relaxed); + if (__builtin_expect(val == -1, 0)) { + // Cold path: Initialize from ENV + const char* e = getenv("HAKMEM_FASTLANE_DIRECT"); + int enable = (e && *e && *e != '0') ? 1 : 0; + atomic_store_explicit(&g_fastlane_direct_enabled, enable, memory_order_relaxed); + return enable; + } + return val; +} + +// Refresh from ENV: Called during benchmark ENV reloads +// Allows runtime toggle without recompilation +void fastlane_direct_env_refresh_from_env(void); diff --git a/core/box/hak_wrappers.inc.h b/core/box/hak_wrappers.inc.h index d7deb3ec..dbbfbe78 100644 --- a/core/box/hak_wrappers.inc.h +++ b/core/box/hak_wrappers.inc.h @@ -43,6 +43,7 @@ void* realloc(void* ptr, size_t size) { #include "malloc_tiny_direct_env_box.h" // Phase 5 E5-4: Malloc Tiny direct path ENV gate #include "malloc_tiny_direct_stats_box.h" // Phase 5 E5-4: Malloc Tiny direct path stats #include "front_fastlane_box.h" // Phase 6: Front FastLane (Layer Collapse) +#include "fastlane_direct_env_box.h" // Phase 19-1: FastLane Direct Path (remove wrapper layer) #include "../hakmem_internal.h" // AllocHeader helpers for diagnostics #include "../hakmem_super_registry.h" // Superslab lookup for diagnostics #include "../superslab/superslab_inline.h" // slab_index_for, capacity @@ -165,6 +166,14 @@ void* malloc(size_t size) { #endif // NDEBUG: malloc_count increment disabled - removes 27.55% bottleneck + // Force libc must override FastLane/hot wrapper paths. + // NOTE: Use the cached file-scope g_force_libc_alloc to avoid getenv recursion + // during early startup (before lock_depth is incremented). + if (__builtin_expect(g_force_libc_alloc == 1, 0)) { + extern void* __libc_malloc(size_t); + return __libc_malloc(size); + } + // Phase 20-2: BenchFast mode (structural ceiling measurement) // WARNING: Bypasses ALL safety checks - benchmark only! // IMPORTANT: Do NOT use BenchFast during preallocation/init to avoid recursion. @@ -176,6 +185,28 @@ void* malloc(size_t size) { // Fallback to normal path for large allocations } + // Phase 19-1b: FastLane Direct Path (bypass wrapper layer, revised) + // Strategy: Direct call to malloc_tiny_fast() (remove wrapper overhead; miss falls through) + // Expected: -17.5 instructions/op, -6.0 branches/op, +10-15% throughput + // ENV: HAKMEM_FASTLANE_DIRECT=0/1 (default: 0, opt-in) + // Phase 19-1b changes: + // 1. Removed __builtin_expect() from fastlane_direct_enabled() check (unfair A/B) + // 2. No change to malloc path (malloc_tiny_fast already optimal) + if (fastlane_direct_enabled()) { + // Fail-fast: match Front FastLane rule (FastLane is only safe after init completes). + if (__builtin_expect(!g_initialized, 0)) { + // Not safe → fall through to wrapper path (handles init/LD safety). + } else { + // Direct path: bypass front_fastlane_try_malloc() wrapper + void* ptr = malloc_tiny_fast(size); + if (__builtin_expect(ptr != NULL, 1)) { + return ptr; // Success: handled by hot path + } + // Not handled → fall through to existing FastLane + wrapper path. + // This preserves lock_depth/init/LD semantics for Mid/Large allocations. + } + } + // Phase 6: Front FastLane (Layer Collapse) // Strategy: Collapse wrapper→gate→policy→route layers into single hot box // Observed: +11.13% on Mixed 10-run (Phase 6 A/B) @@ -631,6 +662,38 @@ void free(void* ptr) { #endif if (!ptr) return; + // Force libc must override FastLane/hot wrapper paths. + // NOTE: Use the cached file-scope g_force_libc_alloc (no getenv) to keep + // this check safe even during early startup/recursion scenarios. + if (__builtin_expect(g_force_libc_alloc == 1, 0)) { + extern void __libc_free(void*); + __libc_free(ptr); + return; + } + + // Phase 19-1b: FastLane Direct Path (bypass wrapper layer, revised) + // Strategy: Direct call to free_tiny_fast() / free_cold() (remove 30% wrapper overhead) + // Expected: -17.5 instructions/op, -6.0 branches/op, +10-15% throughput + // ENV: HAKMEM_FASTLANE_DIRECT=0/1 (default: 0, opt-in) + // Phase 19-1b changes: + // 1. Removed __builtin_expect() from fastlane_direct_enabled() check (unfair A/B) + // 2. Changed free_tiny_fast_hot() → free_tiny_fast() (use winning path directly) + if (fastlane_direct_enabled()) { + // Fail-fast: match Front FastLane rule (FastLane is only safe after init completes). + if (__builtin_expect(!g_initialized, 0)) { + // Not safe → fall through to wrapper path (handles init/LD safety). + } else { + // Direct path: bypass front_fastlane_try_free() wrapper + if (free_tiny_fast(ptr)) { + return; // Success: handled by hot path + } + // Fallback: cold path handles Mid/Large/external pointers + const wrapper_env_cfg_t* wcfg = wrapper_env_cfg_fast(); + free_cold(ptr, wcfg); + return; + } + } + // Phase 6: Front FastLane (Layer Collapse) - free path // Strategy: Collapse wrapper→gate→classify layers into single hot box // Observed: +11.13% on Mixed 10-run (Phase 6 A/B) diff --git a/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md b/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md index f29c446b..05c2a383 100644 --- a/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md +++ b/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md @@ -1,89 +1,75 @@ -# Phase 17: FORCE_LIBC Gap Validation v1 — A/B Test Results +# Phase 17: FORCE_LIBC Gap Validation v2 — A/B Test Results -**Date**: 2025-12-15 -**Verdict**: ✅ **Case B confirmed** — **Layout / I-cache penalty dominates** +**Date**: 2025-12-16 +**Verdict**: ✅ **Case A confirmed** — allocator delta dominates (**libc is ~1.63× faster** in same-binary A/B) --- ## Executive Summary -Phase 17 validated the “system malloc is faster than hakmem” observation while avoiding the classic layout/LTO trap by running a **same-binary A/B**: +Phase 17 exists to avoid the classic “different binary layout/LTO” trap by running a **same-binary A/B**. -- Same binary (`bench_random_mixed_hakmem`) with `HAKMEM_FORCE_LIBC_ALLOC=0/1` shows **allocator logic delta is negligible**. -- The large performance gap appears only when comparing to the tiny `bench_random_mixed_system` binary. +**Important correction (v1 invalid):** +`HAKMEM_FORCE_LIBC_ALLOC=1` was previously checked only in late wrapper paths, so the malloc/free hot paths +could return before FORCE_LIBC was observed. This made the “same-binary libc” measurement effectively still +use hakmem for the hot path. -Conclusion: The dominant gap is **binary text size + layout → I-cache thrash + instruction footprint**, not allocator algorithm efficiency. +**Fix (v2):** +Wrappers now bypass directly to `__libc_malloc/__libc_free` when cached `g_force_libc_alloc==1`, *before* +entering FastLane/hot wrapper logic. + +Result: FORCE_LIBC now reflects real libc behavior in the same binary, and the delta is large. --- ## Measurement Setup Workload: -- `bench_random_mixed_*` (Mixed 16–1024B), working set `WS=400` -- Clean ENV baseline via `scripts/run_mixed_10_cleanenv.sh` +- Mixed 16–1024B, `WS=400`, `ITERS=20000000` +- Clean ENV via `scripts/run_mixed_10_cleanenv.sh` -Two comparisons: -1) **Same-binary toggle** (allocator logic delta) -2) **System binary** (layout penalty delta) +Comparisons: +1) **Same binary**: `bench_random_mixed_hakmem` with `HAKMEM_FORCE_LIBC_ALLOC=0/1` +2) **System binary**: `bench_random_mixed_system` (reference; different binary) --- -## Results +## Results (10-run) ### 1) Same-binary A/B (allocator delta) -Binary: `bench_random_mixed_hakmem` -Toggle: `HAKMEM_FORCE_LIBC_ALLOC=0/1` +Binary: `bench_random_mixed_hakmem` -| Mode | Throughput (ops/s) | Delta | -|------|---------------------|-------| -| hakmem (`FORCE_LIBC=0`) | 48.12M | — | -| libc (`FORCE_LIBC=1`) | 48.31M | **+0.39%** | +| Mode | Mean (ops/s) | Median (ops/s) | Delta | +|------|--------------:|---------------:|------:| +| hakmem (`FORCE_LIBC=0`) | 48.99M | 49.28M | — | +| libc (`FORCE_LIBC=1`) | 79.72M | 80.09M | **+62.7%** | -Interpretation: allocator logic delta is ~noise-level in this experiment context. +Interpretation: the allocator delta is **not** noise-level; libc is materially faster on this workload. -### 2) System binary (layout penalty) +### 2) System binary (layout/wrapper penalty estimate) Binary: `bench_random_mixed_system` -| Mode | Throughput (ops/s) | Delta vs libc-in-hakmem-binary | -|------|---------------------|--------------------------------| -| system malloc | 83.85M | **+73.57%** | +| Mode | Mean (ops/s) | Median (ops/s) | Delta vs libc-in-hakmem-binary | +|------|--------------:|---------------:|--------------------------------:| +| system malloc | 88.06M | 88.35M | **+10.5%** | -Total observed gap: ~+74% class. - ---- - -## Perf Stat (200M iterations) — Smoking Gun - -| Metric | hakmem binary | system binary | Delta | -|--------|---------------|---------------|-------| -| I-cache misses | 153K | 68K | **-55%** | -| Cycles | 17.9B | 10.2B | **-43%** | -| Instructions | 41.3B | 21.5B | **-48%** | -| Binary size | 653K | 21K | **-97%** | - -Interpretation: -- The system binary executes roughly **half the instructions**, with **far fewer I-cache misses**. -- The 30× text footprint difference strongly correlates with the gap. +Interpretation: there is still a non-trivial **“in-hakmem-binary” penalty** (~10%), likely from wrapper/bench +overhead and text footprint, but it is *not* the dominant term versus hakmem’s allocator gap. --- ## Conclusion -Phase 12’s “system malloc is 1.6× faster” observation was real, but the root cause was misattributed: - -- ❌ Not primarily allocator algorithm differences -- ✅ **Text/layout + I-cache locality + instruction footprint** - -This shifts the optimization frontier: -- Stop chasing more routing/dispatch micro-opt (Phase 14–16 plateau) -- Focus on **Hot Text Isolation / layout control** +- ✅ Same-binary `FORCE_LIBC` A/B (v2) shows the **dominant gap is allocator work**, not layout alone. +- ✅ There is also a smaller (~10%) penalty attributable to the hakmem-binary wrapper/text environment. --- ## Next -Proceed to: -- `docs/analysis/PHASE18_HOT_TEXT_ISOLATION_1_NEXT_INSTRUCTIONS.md` - +- Freeze Phase 18 v1 (`--gc-sections`) as NO-GO remains correct. +- Re-evaluate Phase 18 v2 (BENCH_MINIMAL) expectations: -5% instructions is not enough to close a +62% gap. +- Phase 19 should target **structural per-op work reduction** (not dispatch shape), while keeping the FastLane + boundary and “same-binary A/B” discipline. diff --git a/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md b/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md index 738c31ca..6c59026e 100644 --- a/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md +++ b/docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_NEXT_INSTRUCTIONS.md @@ -8,6 +8,13 @@ 本 Phase 17 の目的は、**同一バイナリ内**で `hakmem` vs `libc malloc` を A/B し、gap の実体(allocator差か、バイナリ差か)を SSOT 化すること。 +**重要(v1 の落とし穴)**: +`HAKMEM_FORCE_LIBC_ALLOC=1` が malloc/free の hot path より後でしか観測されないと、FastLane/hot wrapper が先に return してしまい、 +同一バイナリ A/B が **実質 hakmem vs hakmem** になって壊れます。 + +このレポジトリでは 2025-12-16 に `malloc/free` wrapper を修正し、cached `g_force_libc_alloc==1` のときは `__libc_malloc/__libc_free` +へ **最初に** 直行するようにしました(`docs/analysis/PHASE17_FORCE_LIBC_GAP_VALIDATION_1_AB_TEST_RESULTS.md` 参照)。 + --- ## 0. 目的(Deliverables) @@ -127,4 +134,3 @@ perf stat -e cycles,instructions,branches,branch-misses,cache-misses,dTLB-load-m - A/B は **同一バイナリ**で行う(layout/LTO 差で誤判定しない) - 新しい最適化は必ず ENV gate(戻せる)+ 境界 1 箇所 - 迷ったら “Fail-Fast で fallback” を優先(速度より整合性) - diff --git a/docs/analysis/PHASE19_1B_FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md b/docs/analysis/PHASE19_1B_FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md new file mode 100644 index 00000000..6db948c4 --- /dev/null +++ b/docs/analysis/PHASE19_1B_FASTLANE_DIRECT_REVISED_AB_TEST_RESULTS.md @@ -0,0 +1,307 @@ +# Phase 19-1b: FastLane Direct (Revised) A/B Test Results + +**Date**: 2025-12-15 +**Status**: ✅ **GO** (+5.88% throughput) +**Branch**: master +**Commit**: (pending) + +--- + +## Executive Summary + +Phase 19-1 の修正版(19-1b)が成功。Phase 19-1 が NO-GO(-3.81%)となった原因を特定し、修正により **+5.88% throughput** を達成。 + +**犯人特定**: +1. `__builtin_expect(fastlane_direct_enabled(), 0)` が分岐予測を逆効果にしていた +2. `free_tiny_fast_hot()` より `free_tiny_fast()` が勝ち筋(unified cache winner) + +**修正内容**: +- `__builtin_expect()` 削除(フェアな A/B 比較) +- `free_tiny_fast_hot()` → `free_tiny_fast()` 変更(直接勝ち筋を呼ぶ) + +--- + +## A/B Test Results + +### Throughput (10-run benchmark) + +**Baseline (FASTLANE_DIRECT=0)**: +- Mean: **49.17M ops/s** +- StdDev: 407,748 ops/s +- CV: 0.83% + +**Optimized (FASTLANE_DIRECT=1)**: +- Mean: **52.06M ops/s** +- StdDev: 404,146 ops/s +- CV: 0.78% + +**Delta**: **+5.88%** (GO判定、+5%目標クリア) + +--- + +## perf stat Analysis (200M ops) + +### Metrics Table + +| Metric | Baseline | Optimized | Delta | Judgment | +|-----------------------|-----------------|-----------------|------------|----------| +| **Throughput** | 49.17M ops/s | 52.06M ops/s | **+5.88%** | **GO** | +| Cycles | 17,775,213,215 | 16,873,451,633 | -5.07% | Good | +| Instructions | 39,980,185,471 | 33,889,807,627 | **-15.23%** | **Excellent** | +| L1-icache-load-misses | 111,712 | 98,542 | -11.79% | Good | +| iTLB-load-misses | 26,039 | 36,835 | +41.46% | Bad | +| dTLB-load-misses | 59,329 | 76,626 | +29.15% | Bad | +| Branches | 10,297,849,396 | 8,304,201,436 | **-19.36%** | **Excellent** | +| Branch-misses | 232,502,367 | 232,239,642 | -0.11% | Good | + +### Per-Operation Metrics + +| Metric | Baseline | Optimized | Delta | +|--------------|----------|-----------|-----------| +| Cycles/op | 88.88 | 84.37 | **-4.51** | +| Instr/op | 199.90 | 169.45 | **-30.45** | +| Branches/op | 51.49 | 41.52 | **-9.97** | + +**Key Findings**: +- **Instructions: -30.45/op** (-15.23%) → wrapper overhead 削減が効果的 +- **Branches: -9.97/op** (-19.36%) → 分岐数の大幅削減 +- **Cycles: -4.51/op** (-5.07%) → 総合的な効率改善 + +**Trade-offs**: +- iTLB/dTLB misses が悪化したが、instruction/branch 削減の効果が上回った +- Front-end (I-cache) は改善、Backend (dTLB) は悪化 +- 総合的に throughput +5.88% で GO 判定 + +--- + +## Root Cause Analysis: Phase 19-1 が NO-GO となった理由 + +### Phase 19-1 の問題点 + +**Phase 19-1 実装** (`core/box/hak_wrappers.inc.h` 旧版): +```c +// malloc() +if (__builtin_expect(fastlane_direct_enabled(), 0)) { // ← 問題1: expect(...,0) + void* ptr = malloc_tiny_fast(size); + if (__builtin_expect(ptr != NULL, 1)) return ptr; + // ... +} + +// free() +if (__builtin_expect(fastlane_direct_enabled(), 0)) { // ← 問題1: expect(...,0) + if (free_tiny_fast_hot(ptr)) return; // ← 問題2: _hot variant + // ... +} +``` + +**問題の本質**: + +1. **__builtin_expect(..., 0) が逆効果**: + - `fastlane_direct_enabled()` は ENV 変数で制御されるため、A/B test 時に動的に切り替わる + - `__builtin_expect(..., 0)` は「この分岐は unlikely」と CPU に指示 + - → A=0, B=1 で分岐予測が逆になり、フェアな比較にならない + - → B 側(FASTLANE_DIRECT=1)で分岐予測ミスが増加 + +2. **free_tiny_fast_hot() より free_tiny_fast() が勝ち筋**: + - `free_tiny_fast_hot()`: hot/cold split version(Phase 7 で導入) + - `free_tiny_fast()`: monolithic version(Phase 6 winner) + - Phase 9/10 の A/B で `free_tiny_fast()` が勝利していた + - → Phase 19-1 で `_hot` を選択したのは誤り + +### Phase 19-1b の修正 + +**Phase 19-1b 実装** (`core/box/hak_wrappers.inc.h` 修正後): +```c +// malloc() +if (fastlane_direct_enabled()) { // ← 修正1: __builtin_expect 削除 + void* ptr = malloc_tiny_fast(size); + if (__builtin_expect(ptr != NULL, 1)) return ptr; + // ... +} + +// free() +if (fastlane_direct_enabled()) { // ← 修正1: __builtin_expect 削除 + if (free_tiny_fast(ptr)) return; // ← 修正2: free_tiny_fast() に変更 + // ... +} +``` + +**修正の効果**: +1. `__builtin_expect()` 削除 → A/B がフェアな比較に +2. `free_tiny_fast()` 直呼び → 勝ち筋を直接利用 + +**結果**: -3.81% → **+5.88%** (9.69% の改善) + +--- + +## Design Intent vs Implementation Gap + +### Original Design (Phase 19 DESIGN.md) + +**想定**: +- Wrapper layer 削除で -17.5 instructions/op, -6.0 branches/op +- Target: +10-15% throughput + +**実測 (Phase 19-1b)**: +- Instructions: **-30.45/op** (-15.23%, 想定の1.74倍) +- Branches: **-9.97/op** (-19.36%, 想定の1.66倍) +- Throughput: **+5.88%** (想定の半分だが、GO判定) + +**Gap 分析**: +- Instructions/Branches の削減は想定以上 +- しかし throughput は想定の半分(+5.88% vs +10-15%) +- 原因: iTLB/dTLB misses の悪化が throughput を抑制 +- 結論: Instruction 削減だけでは throughput は直線的に改善しない + +--- + +## Lessons Learned + +### 1. __builtin_expect() の落とし穴 + +**問題**: +- ENV-gated path で `__builtin_expect(..., 0)` を使うと A/B がフェアでない +- 動的に切り替わる条件には使うべきでない + +**推奨**: +- Compile-time constant なら OK(例: `HAKMEM_BUILD_RELEASE`) +- Runtime ENV variables には使わない +- A/B test 前に expect hint を削除して検証 + +### 2. Variant 選択の重要性 + +**教訓**: +- `free_tiny_fast_hot()` vs `free_tiny_fast()` の選択が throughput に影響 +- 過去の A/B 結果(Phase 9/10)を参照すべきだった +- 新しい最適化でも「勝ち筋」を選ぶこと + +### 3. Front-end vs Backend Trade-off + +**発見**: +- Instructions/Branches 削減(front-end 改善)は throughput に直結しない +- dTLB misses(backend 悪化)が throughput を抑制 +- 総合バランスが重要 + +**今後の指針**: +- perf stat で front-end/backend を個別に分析 +- Trade-off を明示的に評価 + +--- + +## Verdict: GO + +**Reasons**: +1. **Throughput: +5.88%** (exceeds +5% target) +2. **Instructions: -15.23%** (excellent reduction) +3. **Branches: -19.36%** (excellent reduction) +4. **Cycles: -5.07%** (solid improvement) +5. **I-cache: -11.79%** (front-end improvement) + +**Trade-offs (Acceptable)**: +- iTLB: +41.46% (front-end cost) +- dTLB: +29.15% (backend cost) +- → Overall gain (+5.88%) outweighs these costs + +**Decision**: Phase 19-1b を本線採用。ENV: `HAKMEM_FASTLANE_DIRECT=1` で運用。 + +--- + +## Next Steps + +### Immediate Actions + +1. ✅ Commit Phase 19-1b changes to master +2. ✅ Update CURRENT_TASK.md with results +3. ✅ Archive this report to `docs/analysis/` + +### Future Optimizations + +**Phase 19-2 候補** (dTLB miss 削減): +- TLB prefetch hints +- Page alignment optimization +- Working set size reduction + +**Phase 19-3 候補** (instruction 削減): +- ENV snapshot consolidation (Candidate B) +- Stats counter removal (Candidate C) +- Header validation inline (Candidate D) + +**Target**: Close remaining gap to libc (73 instructions/op → 40-50 instructions/op) + +--- + +## Appendix: Raw Data + +### Baseline (FASTLANE_DIRECT=0) 10-run + +``` +Run 1: 49.70M ops/s +Run 2: 49.10M ops/s +Run 3: 48.83M ops/s +Run 4: 49.24M ops/s +Run 5: 49.29M ops/s +Run 6: 48.54M ops/s +Run 7: 49.77M ops/s +Run 8: 48.52M ops/s +Run 9: 49.32M ops/s +Run 10: 49.37M ops/s + +Mean: 49.17M ops/s +StdDev: 407,748 ops/s +CV: 0.83% +``` + +### Optimized (FASTLANE_DIRECT=1) 10-run + +``` +Run 1: 51.44M ops/s +Run 2: 52.56M ops/s +Run 3: 51.71M ops/s +Run 4: 52.30M ops/s +Run 5: 51.73M ops/s +Run 6: 51.96M ops/s +Run 7: 52.48M ops/s +Run 8: 51.44M ops/s +Run 9: 51.96M ops/s +Run 10: 52.46M ops/s + +Mean: 52.06M ops/s +StdDev: 404,146 ops/s +CV: 0.78% +``` + +### perf stat Baseline (FASTLANE_DIRECT=0) + +``` +Performance counter stats for 'env -i PATH= HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE HAKMEM_FASTLANE_DIRECT=0 ./bench_random_mixed_hakmem 200000000 400 1': + + 17,775,213,215 cycles + 39,980,185,471 instructions # 2.25 insn per cycle + 111,712 L1-icache-load-misses + 26,039 iTLB-load-misses + 59,329 dTLB-load-misses + 10,297,849,396 branches + 232,502,367 branch-misses # 2.26% of all branches + + 4.486849039 seconds time elapsed +``` + +### perf stat Optimized (FASTLANE_DIRECT=1) + +``` +Performance counter stats for 'env -i PATH= HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE HAKMEM_FASTLANE_DIRECT=1 ./bench_random_mixed_hakmem 200000000 400 1': + + 16,873,451,633 cycles + 33,889,807,627 instructions # 2.01 insn per cycle + 98,542 L1-icache-load-misses + 36,835 iTLB-load-misses + 76,626 dTLB-load-misses + 8,304,201,436 branches + 232,239,642 branch-misses # 2.80% of all branches + + 4.247212223 seconds time elapsed +``` + +--- + +**END OF REPORT** diff --git a/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md b/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md new file mode 100644 index 00000000..83d16489 --- /dev/null +++ b/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_1_DESIGN.md @@ -0,0 +1,543 @@ +# Phase 19: FastLane Instruction Reduction - Design Document + +## 0. Executive Summary + +**Goal**: Reduce instruction/branch count gap between hakmem and libc to close throughput gap +**Current Gap**: hakmem 44.88M ops/s vs libc 77.62M ops/s (+73.0% advantage for libc) +**Target**: Reduce instruction gap from +53.8% to <+25%, targeting +15-25% throughput improvement +**Success Criteria**: Achieve 52-56M ops/s (from current 44.88M ops/s) + +### Key Findings + +Per-operation overhead comparison (200M ops): + +| Metric | hakmem | libc | Delta | Delta % | +|--------|--------|------|-------|---------| +| **Instructions/op** | 209.09 | 135.92 | +73.17 | **+53.8%** | +| **Branches/op** | 52.33 | 22.93 | +29.40 | **+128.2%** | +| Cycles/op | 96.48 | 54.69 | +41.79 | +76.4% | +| Branch-miss % | 2.22% | 2.87% | -0.65% | Better | + +**Critical insight**: hakmem executes **73 extra instructions** and **29 extra branches** per operation vs libc. +This massive overhead accounts for the entire throughput gap. + +--- + +## 1. Gap Analysis (Per-Operation Breakdown) + +### 1.1 Instruction Gap: +73.17 instructions/op (+53.8%) + +This excess comes from multiple layers of overhead: +- **FastLane wrapper checks**: ENV gates, class mask validation, size checks +- **Policy snapshot overhead**: TLS reads for routing decisions (3+ reads even with ENV snapshot) +- **Route determination**: Static route table lookup vs direct path +- **Multiple ENV gates**: Scattered throughout hot path (DUALHOT, LEGACY_DIRECT, C7_ULTRA, etc.) +- **Stats counters**: Atomic increments on hot path (FREE_PATH_STAT_INC, ALLOC_GATE_STAT_INC, etc.) +- **Header validation duplication**: FastLane + free_tiny_fast both validate header + +### 1.2 Branch Gap: +29.40 branches/op (+128.2%) + +Branching is **2.3x worse** than instruction gap: +- **Cascading ENV checks**: Each layer adds 1-2 branches (g_initialized, class_mask, DUALHOT, C7_ULTRA, LEGACY_DIRECT) +- **Route dispatch**: Static route check + route_kind switch +- **Early-exit patterns**: Multiple if-checks for ULTRA/DUALHOT/LEGACY paths +- **Stats gating**: `if (__builtin_expect(...))` patterns around counters + +### 1.3 Why Cycles/op Gap is Smaller Than Expected + +Despite +76.4% cycle gap, the CPU is achieving 2.17 IPC (hakmem) vs 2.49 IPC (libc). +This suggests: +- **Good CPU pipelining**: Branch predictor is working well (2.22% miss rate) +- **I-cache locality**: Code is reasonably compact despite extra instructions +- **But**: We're paying for every extra branch in pipeline stalls + +--- + +## 2. Hot Path Breakdown (perf report) + +Top 10 hot functions (% of cycles): + +| Function | % time | Category | Reduction Target? | +|----------|--------|----------|-------------------| +| **front_fastlane_try_free** | 23.97% | Wrapper | ✓ **YES** (remove layer) | +| **malloc** | 23.84% | Wrapper | ✓ **YES** (remove layer) | +| main | 22.02% | Benchmark | (baseline) | +| **free** | 6.82% | Wrapper | ✓ **YES** (remove layer) | +| unified_cache_push | 4.44% | Core | Optimize later | +| tiny_header_finalize_alloc | 4.34% | Core | Optimize later | +| tiny_c7_ultra_alloc | 3.38% | Core | Optimize later | +| tiny_c7_ultra_free | 2.07% | Core | Optimize later | +| hakmem_env_snapshot_enabled | 1.22% | ENV | ✓ **YES** (eliminate checks) | +| hak_super_lookup | 0.98% | Core | Optimize later | + +**Critical observation**: The top 3 user-space functions are **all wrappers**: +- `front_fastlane_try_free` (23.97%) + `free` (6.82%) = **30.79%** on free wrappers +- `malloc` (23.84%) on alloc wrapper +- Combined wrapper overhead: **~54-55%** of all cycles + +### 2.1 front_fastlane_try_free Annotated Breakdown + +From `perf annotate`, the hot path has these expensive operations: + +**Header validation** (lines 1c786-1c791, ~3% samples): +```asm +movzbl -0x1(%rbp),%ebx # Load header byte +mov %ebx,%eax # Copy to eax +and $0xfffffff0,%eax # Extract magic (0xA0) +cmp $0xa0,%al # Check magic +jne ... (fallback) # Branch on mismatch +``` + +**ENV snapshot checks** (lines 1c7ff-1c822, ~7% samples): +```asm +cmpl $0x1,0x628fa(%rip) # g_hakmem_env_snapshot_ctor_mode (3.01%) +mov 0x628ef(%rip),%r15d # g_hakmem_env_snapshot_gate (1.36%) +je ... +cmp $0xffffffff,%r15d +je ... (init path) +test %r15d,%r15d +jne ... (snapshot path) +``` + +**Class routing overhead** (lines 1c7d1-1c7fb, ~3% samples): +```asm +mov 0x6299c(%rip),%r15d # g.5.lto_priv.0 (policy gate) +cmp $0x1,%r15d +jne ... (fallback) +movzbl 0x6298f(%rip),%eax # g_mask.3.lto_priv.0 +cmp $0xff,%al +je ... (all-classes path) +movzbl %al,%r9d +bt %r13d,%r9d # Bit test class mask +jae ... (fallback) +``` + +**Total overhead**: ~15-20% of cycles in front_fastlane_try_free are spent on: +- Header validation (already done again in free_tiny_fast) +- ENV snapshot probing +- Policy/route checks + +--- + +## 3. Reduction Candidates (Prioritized by ROI) + +### Candidate A: **Eliminate FastLane Wrapper Layer** (Highest ROI) + +**Problem**: front_fastlane_try_free + free wrappers consume 30.79% of cycles +**Root cause**: Double header validation + ENV checks + class mask checks + +**Proposal**: Direct call to free_tiny_fast() from free() wrapper + +**Implementation**: +```c +// In free() wrapper: +void free(void* ptr) { + if (__builtin_expect(!ptr, 0)) return; + + // Phase 19-A: Direct call (no FastLane layer) + if (free_tiny_fast(ptr)) { + return; // Handled + } + + // Fallback to cold path + free_cold(ptr); +} +``` + +**Reduction estimate**: +- **Instructions**: -15-20/op (eliminate duplicate header read, ENV checks, class mask checks) +- **Branches**: -5-7/op (remove FastLane gate checks) +- **Impact**: ~10-15% throughput improvement (remove 30% wrapper overhead) + +**Risk**: **LOW** (free_tiny_fast already has validation + routing logic) + +--- + +### Candidate B: **Consolidate ENV Snapshot Checks** (High ROI) + +**Problem**: ENV snapshot is checked **3+ times per operation**: +1. FastLane entry: `g_initialized` check +2. Route determination: `hakmem_env_snapshot_enabled()` check +3. Route-specific: `tiny_c7_ultra_enabled_env()` check +4. Legacy fallback: Another ENV snapshot check + +**Proposal**: Single ENV snapshot read at entry, pass context down + +**Implementation**: +```c +// Phase 19-B: ENV context struct +typedef struct { + bool c7_ultra_enabled; + bool dualhot_enabled; + bool legacy_direct_enabled; + SmallRouteKind route_kind[8]; // Pre-computed routes +} FastLaneCtx; + +static __thread FastLaneCtx g_fastlane_ctx = {0}; +static __thread int g_fastlane_ctx_init = 0; + +static inline const FastLaneCtx* fastlane_ctx_get(void) { + if (__builtin_expect(g_fastlane_ctx_init == 0, 0)) { + // One-time init per thread + const HakmemEnvSnapshot* env = hakmem_env_snapshot(); + g_fastlane_ctx.c7_ultra_enabled = env->tiny_c7_ultra_enabled; + // ... populate other fields + g_fastlane_ctx_init = 1; + } + return &g_fastlane_ctx; +} +``` + +**Reduction estimate**: +- **Instructions**: -8-12/op (eliminate redundant TLS reads) +- **Branches**: -3-5/op (single init check instead of multiple) +- **Impact**: ~5-8% throughput improvement + +**Risk**: **MEDIUM** (need to handle ENV changes during runtime - use invalidation hook) + +--- + +### Candidate C: **Remove Stats Counters from Hot Path** (Medium ROI) + +**Problem**: Stats counters on hot path add atomic increments: +- `FRONT_FASTLANE_STAT_INC(free_total)` (every op) +- `FREE_PATH_STAT_INC(total_calls)` (every op) +- `ALLOC_GATE_STAT_INC(total_calls)` (every alloc) +- `tiny_front_free_stat_inc(class_idx)` (every free) + +**Proposal**: Make stats DEBUG-only or sample-based (1-in-N) + +**Implementation**: +```c +// Phase 19-C: Sampling-based stats +#if !HAKMEM_BUILD_RELEASE + static __thread uint32_t g_stat_counter = 0; + if (__builtin_expect((++g_stat_counter & 0xFFF) == 0, 0)) { + // Sample 1-in-4096 operations + FRONT_FASTLANE_STAT_INC(free_total); + } +#endif +``` + +**Reduction estimate**: +- **Instructions**: -4-6/op (remove atomic increments) +- **Branches**: -2-3/op (remove `if (__builtin_expect(...))` checks) +- **Impact**: ~3-5% throughput improvement + +**Risk**: **LOW** (stats already compile-time optional) + +--- + +### Candidate D: **Inline Header Validation** (Medium ROI) + +**Problem**: Header validation happens twice: +1. FastLane wrapper: `*((uint8_t*)ptr - 1)` (lines 179-191 in front_fastlane_box.h) +2. free_tiny_fast: Same check (lines 598-605 in malloc_tiny_fast.h) + +**Proposal**: Trust FastLane validation, remove duplicate check + +**Implementation**: +```c +// Phase 19-D: Add "trusted" variant +static inline int free_tiny_fast_trusted(void* ptr, int class_idx, void* base) { + // Skip header validation (caller already validated) + // Direct to route dispatch + ... +} + +// In FastLane: +uint8_t header = *((uint8_t*)ptr - 1); +int class_idx = header & 0x0F; +void* base = tiny_user_to_base_inline(ptr); +return free_tiny_fast_trusted(ptr, class_idx, base); +``` + +**Reduction estimate**: +- **Instructions**: -3-5/op (remove duplicate header load + extract) +- **Branches**: -1-2/op (remove duplicate magic check) +- **Impact**: ~2-3% throughput improvement + +**Risk**: **MEDIUM** (need to ensure all callers validate header) + +--- + +### Candidate E: **Static Route Table Optimization** (Lower ROI) + +**Problem**: Route determination uses TLS lookups + bit tests: +```c +if (tiny_static_route_ready_fast()) { + route_kind = tiny_static_route_get_kind_fast(class_idx); +} else { + route_kind = tiny_policy_hot_get_route(class_idx); +} +``` + +**Proposal**: Pre-compute common routes at init, inline direct paths + +**Implementation**: +```c +// Phase 19-E: Route fast path (C0-C3 LEGACY, C7 ULTRA) +static __thread uint8_t g_route_fastmap = 0; // bit 0=C0...bit 7=C7, 1=LEGACY + +static inline bool is_legacy_route_fast(int class_idx) { + return (g_route_fastmap >> class_idx) & 1; +} +``` + +**Reduction estimate**: +- **Instructions**: -3-4/op (replace function call with bit test) +- **Branches**: -1-2/op (replace nested if with single bit test) +- **Impact**: ~2-3% throughput improvement + +**Risk**: **LOW** (route table is already static) + +--- + +## 4. Combined Impact Estimate + +Assuming independent reductions (conservative estimate with 80% efficiency due to overlap): + +| Candidate | Instructions/op | Branches/op | Throughput | +|-----------|-----------------|-------------|------------| +| Baseline | 209.09 | 52.33 | 44.88M ops/s | +| **A: Remove FastLane layer** | -17.5 | -6.0 | +12% | +| **B: ENV snapshot consolidation** | -10.0 | -4.0 | +6% | +| **C: Stats removal (Release)** | -5.0 | -2.5 | +4% | +| **D: Inline header validation** | -4.0 | -1.5 | +2% | +| **E: Static route fast path** | -3.5 | -1.5 | +2% | +| **Combined (80% efficiency)** | **-32.0** | **-12.4** | **+21%** | + +**Projected outcome**: +- Instructions/op: 209.09 → **177.09** (vs libc 135.92, gap reduced from +53.8% to +30.3%) +- Branches/op: 52.33 → **39.93** (vs libc 22.93, gap reduced from +128.2% to +74.1%) +- Throughput: 44.88M → **54.3M ops/s** (vs libc 77.62M, gap reduced from +73.0% to +43.0%) + +**Achievement vs Goal**: ✓ Exceeds target (+21% vs +15-25% goal) + +--- + +## 5. Implementation Plan + +### Phase 19-1: Remove FastLane Wrapper Layer (A) +**Priority**: P0 (highest ROI) +**Effort**: 2-3 hours +**Risk**: Low (free_tiny_fast already complete) + +Steps: +1. Modify `free()` wrapper to directly call `free_tiny_fast(ptr)` +2. Modify `malloc()` wrapper to directly call `malloc_tiny_fast(size)` +3. Measure: Expect +10-15% throughput +4. Fallback: Keep FastLane as compile-time option + +### Phase 19-2: ENV Snapshot Consolidation (B) +**Priority**: P1 (high ROI, moderate risk) +**Effort**: 4-6 hours +**Risk**: Medium (ENV invalidation needed) + +Steps: +1. Create `FastLaneCtx` struct with pre-computed ENV state +2. Add TLS cache with invalidation hook +3. Replace scattered ENV checks with single context read +4. Measure: Expect +5-8% throughput on top of Phase 19-1 +5. Fallback: ENV-gate new path (HAKMEM_FASTLANE_ENV_CTX=1) + +### Phase 19-3: Stats Removal (C) + Header Inline (D) +**Priority**: P2 (medium ROI, low risk) +**Effort**: 2-3 hours +**Risk**: Low (already compile-time optional) + +Steps: +1. Make stats sample-based (1-in-4096) in Release builds +2. Add `free_tiny_fast_trusted()` variant (skip header validation) +3. Measure: Expect +3-5% throughput on top of Phase 19-2 +4. Fallback: Compile-time flags for both features + +### Phase 19-4: Static Route Fast Path (E) +**Priority**: P3 (lower ROI, polish) +**Effort**: 2-3 hours +**Risk**: Low (route table is static) + +Steps: +1. Add `g_route_fastmap` TLS cache +2. Replace function calls with bit tests +3. Measure: Expect +2-3% throughput on top of Phase 19-3 +4. Fallback: Keep existing path as fallback + +--- + +## 6. Box Theory Compliance + +### Boundary Preservation +- **L0 (ENV)**: Keep existing ENV gates, add new ones for each optimization +- **L1 (Hot inline)**: free_tiny_fast(), malloc_tiny_fast() remain unchanged +- **L2 (Cold fallback)**: free_cold(), malloc_cold() remain unchanged +- **L3 (Stats)**: Make optional via #if guards + +### Reversibility +- Each phase is ENV-gated (can revert at runtime) +- Compile-time fallback preserved (HAKMEM_BUILD_RELEASE controls stats) +- FastLane layer can be kept as compile-time option for A/B testing + +### Incremental Rollout +- Phase 19-1: Remove wrapper (default ON) +- Phase 19-2: ENV context (default OFF, opt-in for testing) +- Phase 19-3: Stats/header (default ON in Release, OFF in Debug) +- Phase 19-4: Route fast path (default ON) + +--- + +## 7. Validation Checklist + +After each phase: +- [ ] Run perf stat (compare instructions/branches/cycles per-op) +- [ ] Run perf record + annotate (verify hot path reduction) +- [ ] Run benchmark suite (Mixed, C6-heavy, C7-heavy) +- [ ] Check correctness (Larson, multithreaded, stress tests) +- [ ] Measure RSS/memory overhead (should be unchanged) +- [ ] A/B test (ENV toggle to verify reversibility) + +Success criteria: +- [ ] Throughput improvement matches estimate (±20%) +- [ ] Instruction count reduction matches estimate (±20%) +- [ ] Branch count reduction matches estimate (±20%) +- [ ] No correctness regressions (all tests pass) +- [ ] No memory overhead increase (RSS unchanged) + +--- + +## 8. Risk Assessment + +### High-Risk Areas +1. **ENV invalidation** (Phase 19-2): Runtime ENV changes could break cached context + - Mitigation: Use invalidation hooks (existing hakmem_env_snapshot infrastructure) + - Fallback: Revert to scattered ENV checks + +2. **Header validation trust** (Phase 19-3D): Skipping validation could miss corruption + - Mitigation: Keep validation in Debug builds, extensive testing + - Fallback: Compile-time option to keep duplicate checks + +### Medium-Risk Areas +1. **FastLane removal** (Phase 19-1): Could break gradual rollout (class_mask filtering) + - Mitigation: Keep class_mask filtering in FastLane path only (direct path always falls back safely) + - Fallback: Keep FastLane as compile-time option + +### Low-Risk Areas +1. **Stats removal** (Phase 19-3C): Already compile-time optional +2. **Route fast path** (Phase 19-4): Route table is static, no runtime changes + +--- + +## 9. Future Optimization Opportunities (Post-Phase 19) + +After Phase 19 closes the wrapper gap, next targets: + +1. **Unified Cache optimization** (4.44% cycles): + - Reduce cache miss overhead (refill path) + - Optimize LIFO vs ring buffer trade-off + +2. **Header finalization** (4.34% cycles): + - Investigate always_inline for tiny_header_finalize_alloc() + - Reduce metadata writes (defer to batch update) + +3. **C7 ULTRA optimization** (3.38% + 2.07% = 5.45% cycles): + - Investigate TLS cache locality + - Reduce ULTRA push/pop overhead + +4. **Super lookup optimization** (0.98% cycles): + - Already optimized in Phase 12 (mask-based) + - Further reduction may require architectural changes + +**Estimated ceiling**: With all optimizations, could approach ~65-70M ops/s (vs libc 77.62M) +**Remaining gap**: Likely fundamental architectural differences (thread-local vs global allocator) + +--- + +## 10. Appendix: Detailed perf Data + +### 10.1 perf stat Results (200M ops) + +**hakmem (FORCE_LIBC=0)**: +``` +Performance counter stats for 'bench_random_mixed_hakmem ... HAKMEM_FORCE_LIBC_ALLOC=0': + + 19,296,118,430 cycles + 41,817,886,925 instructions # 2.17 insn per cycle + 10,466,190,806 branches + 232,592,257 branch-misses # 2.22% of all branches + 1,660,073 cache-misses + 134,601 L1-icache-load-misses + + 4.913685503 seconds time elapsed +Throughput: 44.88M ops/s +``` + +**libc (FORCE_LIBC=1)**: +``` +Performance counter stats for 'bench_random_mixed_hakmem ... HAKMEM_FORCE_LIBC_ALLOC=1': + + 10,937,550,228 cycles + 27,183,469,339 instructions # 2.49 insn per cycle + 4,586,617,379 branches + 131,515,905 branch-misses # 2.87% of all branches + 767,370 cache-misses + 64,102 L1-icache-load-misses + + 2.835174452 seconds time elapsed +Throughput: 77.62M ops/s +``` + +### 10.2 Top 30 Hot Functions (perf report) + +``` + 23.97% front_fastlane_try_free.lto_priv.0 + 23.84% malloc + 22.02% main + 6.82% free + 4.44% unified_cache_push.lto_priv.0 + 4.34% tiny_header_finalize_alloc.lto_priv.0 + 3.38% tiny_c7_ultra_alloc.constprop.0 + 2.07% tiny_c7_ultra_free + 1.22% hakmem_env_snapshot_enabled.lto_priv.0 + 0.98% hak_super_lookup.part.0.lto_priv.4.lto_priv.0 + 0.85% hakmem_env_snapshot.lto_priv.0 + 0.82% hak_pool_free_v1_slow_impl + 0.59% tiny_front_v3_snapshot_get.lto_priv.0 + 0.30% __memset_avx2_unaligned_erms (libc) + 0.30% tiny_unified_lifo_enabled.lto_priv.0 + 0.28% hak_free_at.constprop.0 + 0.24% hak_pool_try_alloc.part.0 + 0.24% malloc_cold + 0.16% hak_pool_try_alloc_v1_impl.part.0 + 0.14% free_cold.constprop.0 + 0.13% mid_inuse_dec_deferred + 0.12% hak_pool_mid_lookup + 0.12% do_user_addr_fault (kernel) + 0.11% handle_pte_fault (kernel) + 0.11% __mod_memcg_lruvec_state (kernel) + 0.10% do_anonymous_page (kernel) + 0.09% classify_ptr + 0.07% tiny_get_max_size.lto_priv.0 + 0.06% __handle_mm_fault (kernel) + 0.06% __alloc_pages (kernel) +``` + +--- + +## 11. Conclusion + +Phase 19 has **clear, actionable targets** with high ROI: + +1. **Immediate action (Phase 19-1)**: Remove FastLane wrapper layer + - Expected: +10-15% throughput + - Risk: Low + - Effort: 2-3 hours + +2. **Follow-up (Phase 19-2-4)**: ENV consolidation + stats + route optimization + - Expected: +6-11% additional throughput + - Risk: Medium (ENV invalidation) + - Effort: 8-12 hours + +**Combined target**: +21% throughput (44.88M → 54.3M ops/s) +**Gap closure**: Reduce instruction gap from +53.8% to +30.3% vs libc + +This positions hakmem for competitive performance while maintaining safety and Box Theory compliance. diff --git a/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md b/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md new file mode 100644 index 00000000..bf53bf3a --- /dev/null +++ b/docs/analysis/PHASE19_FASTLANE_INSTRUCTION_REDUCTION_2_NEXT_INSTRUCTIONS.md @@ -0,0 +1,64 @@ +# Phase 19-2: FASTLANE_DIRECT Promotion + Rebaseline (Next Instructions) + +## 0. Status (where we are) + +- Phase 19-1b (FASTLANE_DIRECT) is **GO**: throughput **+5.88%** with **-15.23% instr/op** and **-19.36% branches/op**. +- Safety hardening completed: + - `!g_initialized` → direct path is skipped (fail-fast, same rule as Front FastLane). + - malloc miss no longer calls `malloc_cold()` directly; it falls through to the normal wrapper path (preserves `g_hakmem_lock_depth` invariants). + - ENV cache is a single global `_Atomic` so `bench_profile` refresh affects wrappers. + +## 1. Promotion policy (Box Theory) + +- Keep rollback simple: + - `HAKMEM_FASTLANE_DIRECT=0` → disable (fallback to Phase 6 FastLane wrapper path). + - `HAKMEM_FASTLANE_DIRECT=1` → enable (direct `malloc_tiny_fast()` / `free_tiny_fast()` first). +- Promotion level: + - **Preset promotion** (recommended): set `HAKMEM_FASTLANE_DIRECT=1` in `MIXED_TINYV3_C7_SAFE` and `C6_HEAVY_LEGACY_POOLV1` presets. + - Keep **ENV default = 0** (opt-in) until real-world/LD_PRELOAD validation is done. + +## 2. Required verification (same-binary A/B) + +### 2.1 Mixed (10-run, clean env) + +Baseline: +```sh +HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE HAKMEM_FASTLANE_DIRECT=0 scripts/run_mixed_10_cleanenv.sh +``` + +Optimized: +```sh +HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE HAKMEM_FASTLANE_DIRECT=1 scripts/run_mixed_10_cleanenv.sh +``` + +GO/NO-GO: +- GO: mean **+1.0%** or higher +- NEUTRAL: **±1.0%** → keep as preset-only (do not flip global default) +- NO-GO: **≤ -1.0%** → revert preset promotion + +### 2.2 C6-heavy (5-run) + +```sh +HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1 HAKMEM_FASTLANE_DIRECT=0 ./bench_mid_large_mt_hakmem 1 1000000 400 1 +HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1 HAKMEM_FASTLANE_DIRECT=1 ./bench_mid_large_mt_hakmem 1 1000000 400 1 +``` + +## 3. Perf stat capture (root-cause guardrails) + +Run both A/B with: +```sh +perf stat -e cycles,instructions,branches,branch-misses,L1-icache-load-misses,iTLB-load-misses,dTLB-load-misses -- \ + ./bench_random_mixed_hakmem 200000000 400 1 +``` + +Checklist: +- `instructions/op` and `branches/op` must improve (expected) +- iTLB/dTLB misses may worsen; accept only if throughput still improves + +## 4. Next target selection (after promotion) + +After Phase 19-2 is stable, re-run `perf record` on Mixed and choose the next box by **self% ≥ 5%**: +- If `unified_cache_push/pop` rises: focus on **UnifiedCache data-path** (touch fewer cache lines). +- If `tiny_header_finalize_alloc` rises: focus on **header finalize path** (but treat as high NO-GO risk; prior header work was often NEUTRAL). +- If ENV checks reappear in hot path: consider **Phase 19-3 (ENV check consolidation)**, but keep it in a separate research box. + diff --git a/hakmem.d b/hakmem.d index 3e031027..d44ab9da 100644 --- a/hakmem.d +++ b/hakmem.d @@ -178,7 +178,8 @@ hakmem.o: core/hakmem.c core/hakmem.h core/hakmem_build_flags.h \ core/box/front_fastlane_env_box.h core/box/front_fastlane_stats_box.h \ core/box/front_fastlane_alloc_legacy_direct_env_box.h \ core/box/tiny_front_hot_box.h core/box/tiny_front_cold_box.h \ - core/box/smallobject_policy_v7_box.h core/box/../hakmem_internal.h + core/box/smallobject_policy_v7_box.h core/box/fastlane_direct_env_box.h \ + core/box/../hakmem_internal.h core/hakmem.h: core/hakmem_build_flags.h: core/hakmem_config.h: @@ -441,4 +442,5 @@ core/box/front_fastlane_alloc_legacy_direct_env_box.h: core/box/tiny_front_hot_box.h: core/box/tiny_front_cold_box.h: core/box/smallobject_policy_v7_box.h: +core/box/fastlane_direct_env_box.h: core/box/../hakmem_internal.h: diff --git a/perf.data.phase19_hakmem b/perf.data.phase19_hakmem new file mode 100644 index 0000000000000000000000000000000000000000..9e7b9227e59fc2cd9abcc5b27dd34e7c0cb057fb GIT binary patch literal 352048 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 ZyE3!|9-ge?xvf