Major Features: - Debug counter infrastructure for Refill Stage tracking - Free Pipeline counters (ss_local, ss_remote, tls_sll) - Diagnostic counters for early return analysis - Unified larson.sh benchmark runner with profiles - Phase 6-3 regression analysis documentation Bug Fixes: - Fix SuperSlab disabled by default (HAKMEM_TINY_USE_SUPERSLAB) - Fix profile variable naming consistency - Add .gitignore patterns for large files Performance: - Phase 6-3: 4.79 M ops/s (has OOM risk) - With SuperSlab: 3.13 M ops/s (+19% improvement) This is a clean repository without large log files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
Phase 6.25+6.27: Refill Batching + Learner Integration 結果
日付: 2025-10-24 ステータス: ⚠️ 目標未達成(+1.1% のみ、予想 +15-25%) 結論: Architectural issues 発見 → 根本的な再設計が必要
📊 Benchmark 結果
Mid Pool Performance (2KB-32KB, 10s)
1 Thread
| Version | Throughput | vs Baseline | vs Quick wins | vs mimalloc |
|---|---|---|---|---|
| Quick wins (baseline) | 4.03 M/s | baseline | baseline | 27.7% |
| + Phase 6.25 (batch=1) | 3.96 M/s | -1.7% | -1.7% | 27.2% |
| + Phase 6.25 (batch=2) | 3.99 M/s | -1.0% | -1.0% | 27.4% |
| + Phase 6.27 (learner) | 3.92 M/s | -2.7% | -2.7% | 26.9% |
4 Threads
| Version | Throughput | vs Baseline | vs Quick wins | vs mimalloc |
|---|---|---|---|---|
| Quick wins (baseline) | 13.78 M/s | baseline | baseline | 46.7% |
| + Phase 6.25 (batch=1) | 13.53 M/s | -1.8% | -1.8% | 45.8% |
| + Phase 6.25 (batch=2) | 13.68 M/s | -0.7% | -0.7% | 46.4% |
| + Phase 6.27 (learner) | 13.33 M/s | -3.3% | -3.3% | 45.2% |
mimalloc reference: Mid 1T = 14.56 M/s, Mid 4T = 29.50 M/s
Summary
- Phase 6.25 (Refill Batching): batch=1 vs batch=2 で +1.1% のみ(予想 +10-15%)
- Phase 6.27 (Learner): -1.5% 悪化(予想 +5-10%、実際はオーバーヘッド)
🚀 実装内容
Phase 6.25 本体: Refill Batching
目標: Mid Pool refill の batch 化で latency 削減
実装:
alloc_tls_page_batch()関数追加(~70 LOC)- Refill call site を batch allocation に変更(~60 LOC)
- 環境変数
HAKMEM_POOL_REFILL_BATCH=1-4(default 2)
変更ファイル:
hakmem_pool.c: +135 LOC
期待効果: +10-15% (Mid 1T) 実測効果: +1.1% (Mid 4T)
Phase 6.27: Learner Integration
目標: 既存 learner を有効化して adaptive tuning
実装:
- 既存の learner インフラを活用(コード変更なし)
docs/specs/ENV_VARS.mdにドキュメント追加
使い方:
HAKMEM_LEARN=1 \
HAKMEM_TARGET_HIT_MID=0.75 \
HAKMEM_CAP_STEP_MID=8 \
HAKMEM_CAP_MAX_MID=512 \
./your_app
期待効果: +5-10% 実測効果: -1.5% (overhead)
💔 失敗の分析
Phase 6.25 が効かなかった理由
仮説: TLS Ring (32 slots) が十分大きい
検証:
- Ring capacity: 32 slots
- Refill 頻度: Ring が空になる頻度が低い
- 結論: Batch 化しても refill 回数が少ないため効果薄
証拠:
- batch=1 vs batch=2: わずか +1.1%
- batch=4 でも大きな改善は見込めない(refill 自体が稀)
真のボトルネック
Task先生の診断:
- Lock Contention (50% of gap): 56 mutexes が最大ボトルネック
- Hash Table Lookups (25% of gap): Page descriptor lookup が遅い
- Excess Branching (15% of gap): Allocation path が複雑
→ Refill は全体の 5% 未満のボトルネック
Phase 6.27 が悪化した理由
短時間測定での overhead
問題:
- Learner は background thread で 1秒ごとにポーリング
- 10秒測定では適応時間不足
- Background thread の CPU overhead が顕在化
検証:
- 60秒以上の長時間測定が必要
- または、learner を無効化して静的 CAP を使うべき
🔬 mimalloc Deep Analysis による発見
hakmem vs mimalloc 根本的差異
Task先生が作成した 1200行の詳細分析(MIMALLOC_DEEP_ANALYSIS_2025_10_24.md)から:
Architectural 比較
| Feature | hakmem | mimalloc | Gap |
|---|---|---|---|
| Lock usage | 56 mutexes | 0 locks | ∞ |
| Fast path | 20-30 instructions | 7 instructions | 3-4x |
| Branches | 7-10 branches | 2 branches | 3.5-5x |
| Hash lookups | 2-4 per operation | 0 | ∞ |
| Metadata overhead | 0.39-0.98% | 0.12% | 3.25x |
Performance Model
mimalloc fast path (idealized):
Cost = TLS_load + size_check + bin_lookup + page_deref + block_pop
= 1 + 1 + 1 + 1 + 1
= 5 cycles
hakmem fast path (with overhead):
Cost = class_lookup + TC_check + ring_check + ring_pop
+ header_write + page_counter_inc (hash lookup)
= 1 + (2 + hash) + 1 + 1 + 5 + (hash + atomic)
= 10 + 2×hash + atomic
= 10 + 2×(10-20) + 5
= 35-55 cycles
Ratio: hakmem は 7-11× slower per allocation
Lock Contention Model
4 threads, 10M alloc/s, 100ns lock duration:
Contention probability = (threads - 1) × rate × duration / shards
= 3 × 10^7 × 100e-9 / 8
= 37.5%
Blocking cost per contention = 50-200 cycles
Total overhead = 0.375 × 150 = 56 cycles per allocation
結論: Lock contention だけで 50% of gap を説明できる
🎯 4大ボトルネック(Task先生の診断)
| Rank | Bottleneck | hakmem Cost | mimalloc Cost | Impact |
|---|---|---|---|---|
| 1 | Lock Contention | 56 cycles | 0 cycles | 50% 🔥 |
| 2 | Hash Lookups | 20-40 cycles | 0 cycles | 25% |
| 3 | Excess Branching | 5-8 branches | 0 branches | 15% |
| 4 | Metadata Overhead | 5 cycles | 0 cycles | 10% |
Total: hakmem は ~120 cycles/allocation vs mimalloc ~5 cycles/allocation
💡 hakmem の設計思想 再評価
hakmem が目指したもの
7層 TLS caching の意図:
- Burst allocation: Ring (32) + LIFO (256) で吸収
- Locality-aware: Site-based sharding で同じコードパスは同じシャード
- Cross-thread 最適化: Transfer Cache で owner-aware return
- Adaptive: Learning system で CAP/W_MAX 調整
利点が活きる workload:
- Burst pattern: 短時間に大量確保 → すぐ全部解放
- Locality-sensitive: 同じ関数から繰り返し確保
- Producer-Consumer: Thread 間でデータ受け渡し
larson benchmark との相性
larson の特性:
- Allocation pattern: ランダム(burst じゃない)
- Size: ランダム 2KB-32KB(locality 低い)
- Thread: 4 threads 均等負荷(Producer-Consumer じゃない)
→ hakmem の利点が活きにくい
別の workload では:
- JSON parser: Burst → hakmem 有利?
- Database buffer pool: Locality → hakmem 有利?
- Message queue: Producer-Consumer → hakmem 有利?
公平な評価
hakmem の設計は悪くない、ただし:
- Over-engineered: 7層は多すぎ
- Overhead が重い: Hash + Mutex が致命的
- Universal performance: mimalloc の 46.7% は厳しい
改善の方向性:
- Keep: Burst cache, Site-based sharding の思想
- Fix: Hash lookup → Pointer arithmetic
- Fix: Mutex → Lock-free
- Simplify: 7層 → 3層
🚀 次の計画:mimalloc 打倒ロードマップ
目標
Phase 7: Hybrid hakmem
- Universal (larson): mimalloc の 60-75%(目標)
- Burst: mimalloc の 110-120%(hakmem cache 活用)
- Locality: mimalloc の 110-130%(sharding 活用)
3段階アプローチ
Phase 7.1: Quick Fixes (8 hours) → +5-10%
- QF1: Trylock probes 削減(3→1)
- QF2: Ring + LIFO 統合(single cache)
- QF3: Header writes スキップ(fast path)
期待効果: +5-10% (14.5-15.2 M/s)
Phase 7.2: Medium Fixes (20-30 hours) → +25-35%
-
MF1: Lock-free Freelist (+15-25%) ⭐⭐⭐
- 56 mutexes → atomic CAS
- ABA problem 対策
- 実装時間: 12 hours
-
MF2: Pointer Arithmetic Page Lookup (+10-15%)
- Hash table → Segment-based addressing
- mimalloc の技術移植
- 実装時間: 8-10 hours
-
MF3: Allocation Path 簡略化 (+5-8%)
- 7層 → 3層(Ring + Page + Remote)
- Branch 削減
- 実装時間: 8-10 hours
期待効果: +25-35% (17.2-18.6 M/s) → 58-63% of mimalloc
Phase 7.3: Moonshot (60 hours) → +50-70%
MS1: Per-page Sharding(mimalloc 完全移植)
- Global shards → Per-page freelists
- Segment-based memory layout
- 実装時間: 60 hours
期待効果: +50-70% (20.7-23.4 M/s) → 70-79% of mimalloc
📈 Phase 進捗まとめ
| Phase | Strategy | Time | Expected | Actual | Status |
|---|---|---|---|---|---|
| 6.24 | SuperSlab optimization | 4h | +8% | +8.2% | ✅ Success |
| 6.25 Quick | Compiler + Ring + W_MAX + Prefault | 2h | +14-24% | +37.8% | ✅ 大成功 |
| 6.25 本体 | Refill Batching | 2h | +10-15% | +1.1% | ❌ Failed |
| 6.27 | Learner Integration | 0.2h | +5-10% | -1.5% | ❌ Failed |
| 合計 | 8.2h | +29-47% | +37.8% | ⚠️ Plateaued |
現状: Quick wins で大幅改善したが、architectural limit に到達
🎓 Lessons Learned
1. Incremental 最適化の限界
Quick wins (Phase 6.25 Quick): Compiler + TLS Ring 拡張 → +37.8% 🎉
Architectural fixes (Phase 6.25-6.27): Refill batching + Learner → +1.1% 😢
教訓: Low-hanging fruits は取り尽くした。次は architectural redesign が必要
2. Overhead の累積効果
56 cycles (lock) + 30 cycles (hash) + 10 cycles (branches) = 96 cycles overhead
Small overhead × Many operations = Big gap
教訓: Fast path の overhead を 1 cycle でも削るべき
3. 設計思想 vs Universal Performance
hakmem: Adaptive, Locality-aware → Special cases で有利 mimalloc: Simple, Universal → All cases で安定
教訓: Universal performance を犠牲にした最適化は危険
4. Task先生の分析の威力
1200行の詳細分析 → 真のボトルネックを特定
- Lock contention: 50%
- Hash lookups: 25%
- Branching: 15%
- Metadata: 10%
教訓: Profiling より理論分析が有効な場合もある
📂 File Changes
Phase 6.25 本体
| File | Changes | LOC |
|---|---|---|
hakmem_pool.c |
alloc_tls_page_batch() 追加 |
+135 |
docs/specs/ENV_VARS.md |
HAKMEM_POOL_REFILL_BATCH 追加 |
+1 |
Phase 6.27
| File | Changes | LOC |
|---|---|---|
docs/specs/ENV_VARS.md |
Learner env vars documented | +0 (既存) |
Total: ~136 LOC 追加
🎯 Next Actions
Recommended: MF1 (Lock-Free Freelist) を即実装
理由:
- 最大ボトルネック (50% of gap) を直接解決
- Standalone fix: 他の変更に依存しない
- Proven technique: mimalloc, jemalloc で実証済み
- Expected gain: +15-25% (13.78 → 15.8-17.2 M/s)
実装時間: 12 hours
Risk: Medium(ABA problem、memory ordering bugs)
Mitigation:
- Extensive testing
- TSan (ThreadSanitizer)
- Epoch-based reclamation
Alternative: Quick Fixes を先に実装
理由:
- Low risk: 小さな変更
- Quick win: 8 hours で +5-10%
- MF1 の前準備: Code simplification
実装時間: 8 hours
🎬 Conclusion
Phase 6.25+6.27 では +1.1% の微増に留まり、目標未達成でした。
原因: Architectural bottlenecks(Lock, Hash, Branching)
発見: Task先生の深層解析により、真のボトルネックを特定
次のステップ:
- MF1 (Lock-Free) または Quick Fixes を実装
- Hybrid hakmem: mimalloc の技術 + hakmem の思想
mimalloc を倒す道は険しいが、確実に進んでいます! 🔥🔥🔥
作成日: 2025-10-24 14:00 JST ステータス: ✅ Phase 6.25-6.27 完了、Phase 7 へ 次のフェーズ: mimalloc 打倒作戦(MF1 または Quick Fixes)
Appendix: Benchmark Commands
Phase 6.25 測定
# Baseline (batch=1)
env HAKMEM_POOL_REFILL_BATCH=1 LD_PRELOAD=./libhakmem.so \
./mimalloc-bench/bench/larson/larson 10 2048 32768 10000 1 12345 4
# Phase 6.25 (batch=2)
env HAKMEM_POOL_REFILL_BATCH=2 LD_PRELOAD=./libhakmem.so \
./mimalloc-bench/bench/larson/larson 10 2048 32768 10000 1 12345 4
Phase 6.27 測定
# Learner ON
env HAKMEM_POOL_REFILL_BATCH=2 HAKMEM_LEARN=1 \
HAKMEM_TARGET_HIT_MID=0.75 HAKMEM_CAP_STEP_MID=8 \
LD_PRELOAD=./libhakmem.so \
./mimalloc-bench/bench/larson/larson 10 2048 32768 10000 1 12345 4
mimalloc Reference
# mimalloc Mid 4T
LD_PRELOAD=/lib/x86_64-linux-gnu/libmimalloc.so.2 \
./mimalloc-bench/bench/larson/larson 10 2048 32768 10000 1 12345 4
# Result: 29.50 M/s