# HAKMEM Development History ## Phase 5-B-Simple: Dual Free Lists + Magazine Unification (2025-11-02~03) ❌ ### 目標 - Dual Free Lists (mimalloc): +10-15% - Magazine 統合: +3-5% - 合計期待: +15-23% (16.53 → 19.1-20.3 M ops/sec) ### 実装内容 #### 1. TinyUnifiedMag 定義 (hakmem_tiny.c:590-603) ```c typedef struct { void* slots[256]; // Large capacity for better hit rate uint16_t top; // 0..256 uint16_t cap; // =256 (adjustable per class) } TinyUnifiedMag; static int g_unified_mag_enable = 1; static uint16_t g_unified_mag_cap[TINY_NUM_CLASSES] = { 64, 64, 64, 64, // Classes 0-3 (hot): 64 slots 32, 32, 16, 16 // Classes 4-7 (cold): smaller capacity }; static __thread TinyUnifiedMag g_tls_unified_mag[TINY_NUM_CLASSES]; ``` #### 2. Dual Free Lists 追加 (hakmem_tiny.h:147-151) ```c // Phase 5-B: Dual Free Lists (mimalloc-inspired optimization) void* local_free; // Local free list (same-thread, no atomic) atomic_uintptr_t thread_free; // Remote free list (cross-thread, atomic) ``` #### 3. hak_tiny_alloc() 書き換え (hakmem_tiny_alloc.inc:159-180) - 48 lines → 8 lines に削減 - 3-4 branches → 1 branch に削減 ```c if (__builtin_expect(g_unified_mag_enable, 1)) { TinyUnifiedMag* mag = &g_tls_unified_mag[class_idx]; if (__builtin_expect(mag->top > 0, 1)) { void* ptr = mag->slots[--mag->top]; HAK_RET_ALLOC(class_idx, ptr); } // Fast path - try local_free from TLS active slabs (no atomic!) TinySlab* slab = g_tls_active_slab_a[class_idx]; if (!slab) slab = g_tls_active_slab_b[class_idx]; if (slab && slab->local_free) { void* ptr = slab->local_free; slab->local_free = *(void**)ptr; HAK_RET_ALLOC(class_idx, ptr); } } ``` #### 4. Free path 分離 (hakmem_tiny_free.inc) - Same-thread: local_free (no atomic) - lines 216-230 - Remote-thread: thread_free (atomic CAS) - lines 468-484 #### 5. Migration logic (hakmem_tiny_slow.inc:12-76) - local_free → Magazine (batch 32 items) - thread_free → local_free → Magazine #### 6. Magazine refill from SuperSlab (hakmem_tiny_slow.inc:78-107) - Batch allocate 8-64 blocks ### ベンチマーク結果 💥 #### Initial (Magazine cap=256) - bench_random_mixed: 16.51 M ops/sec (baseline: 16.53, -0.12%) #### After Dual Free Lists (Magazine cap=256) - bench_random_mixed: 16.35 M ops/sec (-1.1% vs baseline) #### After local_free fast path (Magazine cap=256) - bench_random_mixed: 16.42 M ops/sec (-0.67% vs baseline) #### After capacity optimization (Magazine cap=64) - bench_random_mixed: 16.36 M ops/sec (-1.0% vs baseline) #### Final evaluation (Magazine cap=64) **Single-threaded (bench_tiny_hot, 64B):** - System allocator: **169.49 M ops/sec** - HAKMEM Phase 5-B: **49.91 M ops/sec** - **Regression: -71%** (3.4x slower!) **Multi-threaded (bench_mid_large_mt, 2 threads, 8-32KB):** - System allocator: **11.51 M ops/sec** - HAKMEM Phase 5-B: **7.44 M ops/sec** - **Regression: -35%** - ⚠️ NOTE: Tests 8-32KB allocations (outside Tiny range) ### 根本原因分析 🔍 #### 1. Magazine capacity ミスチューン - **問題**: 64 slots は ST workload に小さすぎる - **詳細**: batch=100 の場合、2回に1回は slow path に落ちる - **原因**: System allocator の tcache (7+ entries per size) との比較で劣る - **Perf分析**: `hak_tiny_alloc_slow` が 4.25% を占める (高すぎ) #### 2. Migration logic オーバーヘッド - **問題**: Slow path での free list → Magazine migration が高コスト - **詳細**: Batch migration (32 items) が頻繁に発生 - **原因**: Pointer chase + atomic operations の累積 - **Perf分析**: `pthread_mutex_lock` が 3.40% (single-threaded なのに!) #### 3. Dual Free Lists の誤算 - **問題**: ST では効果ゼロ、むしろオーバーヘッド - **詳細**: ST では remote_free は発生しない - **原因**: Dual structures のメモリ overhead のみが残る - **教訓**: MT 専用の最適化を ST に適用した #### 4. Unified Magazine の問題 - **問題**: 統合で simplicity は得たが performance は失った - **詳細**: 旧 HotMag (128 slots) + Fast + Quick の組み合わせのほうが高速 - **原因**: 単純化 ≠ 高速化 - **教訓**: Complexity reduction が performance improvement とは限らない ### 学んだこと 📚 #### ✅ Good Ideas 1. **Magazine unification 自体は良アイデア** (complexity 削減の方向性は正しい) 2. **Dual Free Lists は mimalloc で実証済み** (ただし MT 環境で) 3. **Migration logic の発想** (free list を Magazine に集約) #### ❌ Bad Execution 1. **Capacity tuning が不適切** (64 slots → 128+ 必要) 2. **Dual Free Lists は MT 専用** (ST で導入すべきでない) 3. **Migration logic が重すぎる** (batch size 削減 or lazy migration 必要) 4. **Benchmark mismatch** (ST で MT 最適化を評価した) #### 🎯 Next Time 1. **ST と MT を分けて設計** (条件付きコンパイル or runtime switch) 2. **Capacity を大きめに** (128-256 slots for hot classes) 3. **Migration を軽量化** (lazy migration, smaller batch size) 4. **Benchmark を先に選定** (最適化の方向性と一致させる) ### 関連コミット - 4672d54: refactor(tiny): expose class locks for module sharing - 6593935: refactor(tiny): move magazine init functions - 1b232e1: refactor(tiny): move magazine capacity helpers - 0f1e5ac: refactor(tiny): extract magazine data structures - 85a00a0: refactor(core): organize source files into core/ directory ### 次のステップ候補 1. **Phase 5-B-v2**: Magazine unification のみ (Dual Free Lists なし, capacity 128-256) 2. **Phase 6 系**: L25/SuperSlab 最適化に移行 3. **Rollback**: Baseline に戻って別アプローチ --- ## Phase 5-A: Direct Page Cache (2025-11-01) ❌ ### 目標 - Direct cache でO(1) slab lookup: +15-20% ### 実装内容 - Global `slabs_direct[129]` でO(1) direct page cache ### ベンチマーク結果 💥 - bench_random_mixed: 15.25-16.04 M ops/sec (baseline: 16.53) - **Regression: -3~-7.7%** (期待+15-20% → 実際-3~-7.7%) ### 根本原因 - Global cache による contention - Cache pollution - False sharing ### 学んだこと - Global structures は避けるべき (TLS が基本) - Direct cache よりも Magazine-based approach が有効 --- ## Phase 4-A1: HotMag capacity tuning (2025-10-31) ❌ ### 目標 - HotMag capacity を増やして hit rate 向上 ### 結果 - 性能改善なし ### 学んだこと - Capacity 単体では効果薄い - 構造的な問題を解決する必要 --- ## Phase 3: Remote drain optimization (2025-10-30) ❌ ### 目標 - Remote drain の最適化 ### 結果 - 性能改善なし ### 学んだこと - Remote drain はボトルネックではなかった --- ## Phase 2+1: Magazine + Registry optimizations (2025-10-29) ✅ ### 目標 - Magazine capacity tuning - Registry optimization ### 結果 - **成功**: 性能改善達成 ### 学んだこと - Magazine-based approach は有効 - Registry は O(1) lookup で十分