# Current Task (2025-11-04) ## 🔔 最新アップデート (2025-11-06 23:xx) - Build 既定を Box Refactor(Phase 6-1.7)に切替済み。 - Makefile に `-DHAKMEM_TINY_PHASE6_BOX_REFACTOR=1` を既定付与。 - 旧系へ切替: `make BOX_REFACTOR_DEFAULT=0 larson_hakmem`。 - Larson 2s/4T・5s/4T でセグフォ再発なしを確認(SLL-only, FAST_CAP=16, SS_ADOPT=1)。 - 次フェーズ: mimalloc 対決(Larson)へ移行。Hot Tiny クラス向けの refill/binding 帯域最適化に集中。 推奨計測プロファイル(現時点) - SLL-only Fast: `HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0` - Fast tier: `HAKMEM_TINY_FAST_CAP=16` - Refill: `HAKMEM_TINY_REFILL_COUNT_HOT=64`(A/B: 48/64) - Pipeline: `HAKMEM_TINY_SS_ADOPT=1`(publish→mail→adopt 通電) 再現コマンド(2s/4T, 5s/4T) ``` HAKMEM_TINY_REFILL_COUNT_HOT=64 \ HAKMEM_TINY_FAST_CAP=16 \ HAKMEM_TINY_TRACE_RING=0 HAKMEM_SAFE_FREE=0 \ HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0 \ HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 \ ./larson_hakmem 2 8 128 1024 1 12345 4 ./larson_hakmem 5 8 128 1024 1 12345 4 ``` デバッグ一発ログ(導通確認) ``` HAKMEM_TINY_REFILL_OPT_DEBUG=1 \ HAKMEM_TINY_TRACE_RING=0 HAKMEM_SAFE_FREE=0 \ HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0 \ HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 \ ./larson_hakmem 2 8 128 1024 1 12345 4 ``` ## 🎯 次の主目標(mimalloc 対決: Larson/TinyHot) 1) Hot Tiny リフィル最適化(Box 4 境界の探索コスト縮減) - [ ] `HAKMEM_TINY_REFILL_COUNT_HOT={48,64}` を A/B。L1d miss と IPC を `perf stat` で取得。 - [ ] `tiny_refill_try_fast()` の class<=3 優先化(MailBox→Sticky/Hot の順)と早期 return の徹底。 - [ ] Drain のチェイン splice(済)を維持しつつ、不要ドレインの抑制(remote_pending のみ)。 2) ベンチ・スナップショット - [ ] `scripts/bench_capture_now.sh` で現構成を保存(日付入り)。 - [ ] `scripts/profiles/tinyhot_best.env` に `REFILL_COUNT_HOT`/`FAST_CAP` を追記しベストを固定。 3) 比較と回帰防止 - [ ] Larson 2s/4s × threads=1/4/8 で hakmem vs mimalloc を測定・記録。 - [ ] Guard/ASan を OFF のまま長時間(>30s)を1回通して安定性を確認。 4) 伸びなければ(次の一手) - [ ] class<=3 の探索順再編(Sticky/Hot/Bench の順入替)を小さく A/B。 - [ ] それでも平坦なら、Front(最前段)の前倒しポップ(SLL 事前 pop)を A/B(Box 5 内だけで完結)。 ## 🔔 最新アップデート (2025-11-06 19:40) - RemoteTrack Box(debug-only)を導入し、ノードの状態遷移(alloc→remote→drain→freelist)を追跡。矛盾時は `REMOTE_TRACK_MISMATCH`+BT を出力。 - SlabHandle を全採用経路に統一:所有権取得→`slab_drain_remote_full()`→ゼロ確認→採用失敗時は release。Box2/Box3 の境界を1箇所へ集約。 - TLS alloc 前に remote queue を opportunistic drain(`superslab_alloc_from_slab`)し、side-table 上に残るノードは guard モードで Fail-Fast。 - guard 走行では `REMOTE_TRACK_MISMATCH stage=alloc_ret` がまだ発生。原因は「remote queue が drain されず、採用境界をすり抜けて TLS 返却されたノードが存在」。 - 次手:採用境界に“remote 残があれば採用しない”ゲートを追加し、配布直前に side-table を再確認(guard 時のみ)して強制停止する。 ## 🔔 最新アップデート (2025-11-04 12:20) 直近の構造改善(箱の積み直し v2) 1) SlabHandle Box(core/slab_handle.h, ~100行) - 所有権取得・リモートキュードレイン・メタデータアクセスをカプセル化 - 型安全なハンドル(valid==1 のときのみ drain/modify が可能) - Invariant: drain/push/pop は「所有権取得後のみ」実行可(境界1箇所)。 2) 6 箇所のリファクタリング(SlabHandle 適用) - tiny_refill.h: Sticky / Hot / Bench / Mailbox 採用箇所(4箇所)で、候補決定→SlabHandle 取得→remote_drain→bind の順へ整理 - tiny_mmap_gate.h: Registry scan の採用箇所(1箇所)を SlabHandle 化 - hakmem_tiny_free.inc: SuperSlab adopt path(1箇所)を SlabHandle 化 3) 所有権なし drain のバグ修正 - hakmem_tiny_superslab.h:376 の `ss_remote_drain_light()` が ownership 無しで drain していた点を是正 - 修正: `ss_owner_try_acquire()` で取得に成功した場合のみ `ss_remote_drain_to_freelist()` を実行 現状の問題(継続中) - 依然 crash(fault_addr=0x6261)。Tiny Debug Ring にて以下を観測: - [N] event=free_enter class=0 ptr=0x...28c0 - [N+1] event=free_enter class=3 ptr=0x...28c0 ← 同一ポインタを異なる class で 2 回 free - 結論: freelist 破損ではなく、クラス判定ミス or 二重 free(UAF)の可能性が高い。 - Hypothesis: - hak_super_lookup() の再マップ(旧 SS → 新 SS 同一アドレスで別 class)が、二重 free を“別 class”に見せている。 - 実態は上位レイヤ(呼び出し側)の二重 free である可能性が高い。 - 対応(デバッグ方針): - `HAKMEM_SAFE_FREE=1` を既定ON推奨(デバッグ期間)。 - SS free 境界でブロック整合チェック(slab_base/offset/size/容量)と freelist 簡易スキャン(<=64)で二重 free を検出。 - SS/Tiny の二重ルックアップ比較(両者が同時に見つかり class が不一致ならリングに記録)。 - `HAKMEM_SAFE_FREE_STRICT=1` なら Fail‑Fast(SIGUSR2)で即座に停止。 最優先課題は「FAST_CAP=0(fast-tier OFF)時に 4T Larson で再現する SuperSlab remote free の SEGV」を潰すことです。publish→mail→adopt は通電が確認できており、先に Box 2/3(Remote/Ownership)を箱単位で健全化します。 ### 症状(Larson 2s, 4T, FAST_CAP=0) - `hak_tiny_free_superslab()` → `ss_remote_push()` → `tiny_publish_notify()` あたりで SIGSEGV。`fault_addr` は常に低い値(例: 0x6261)で、invalid ポインタ参照。 - Debug Ring で直前イベントを記録すると、「class=4 の remote free → alloc → free_remote → alloc → …」が循環。ptr は SuperSlab 内に見えるが、キューに積まれた時点で破損疑い。 - FAST_CAP>0 に戻すと crash は発生せず、throughput ≈3.7M ops/s(以前より低下)。publish pipeline がゼロのままのため本質的な性能改善は未着手。 ### 箱構成と境界 1. **FrontGate(fast-tier/Hot/TLS)の箱** - `tiny_fast_pop/push`, `hotmag_pop/push`, TLS SLL/Magazine - ★ 現在の仮説:FAST_CAP=0 で front が完全にバイパスされる際、remote queue への戻し順序が乱れている 2. **RemoteQueue(ss_remote_push/dain)の箱** - SuperSlab remote_heads / remote_counts / slab_listed - `tiny_publish_notify` が通知境界 3. **Mailbox/Publish の箱** - `tiny_mailbox_publish/fetch`, `ss_partial_publish/adopt` 4. **Debug Ring(可視化の箱)** - `HAKMEM_TINY_TRACE_RING=1` で alloc/free/publish 直前イベントをダンプ 現在の crash は FrontGate または RemoteQueue の内部バグが境界越えで露呈している状態 → 境界を固めて中/外どちらに異常があるか見極める。 ### 進行中のタスク 1. **RemoteQueue 安全化(優先)** - [ ] `ss_remote_push()` にポインタ境界チェック(スーパースラブ内か)と Debug Ring ログ追加 - [ ] remote push 後に `tiny_publish_notify` へ渡すフォーマット(ss+slab_idx)を `tiny_mailbox_publish` で検証用トレース - [ ] `ss_remote_drain_to_freelist` 前後で freelist pointer をトレースし、破損発生位置を特定(remote queue 内滞留→再 push の経路切り分け) - [x] `tiny_remote_sentinel_set()` で 0x6261 汚染を一度だけ捕捉し BT + SIGUSR2 を吐くトラップを追加(再現経路の特定用) - [x] remote side table を箱化 (`1<<20` + `tiny_remote_side_clear()` + `side_overflow` フォールバック) し、飽和時でも sentinel/next の整合を維持 - [ ] sentinel 汚染が発生した際の callstack を段階別(set/scan/drain)に収集し、同一ノードへの二重 push(実質 double free)発生箇所を pinpoint 2. **FrontGate バイパス検証** - [ ] `FAST_CAP=0` 時専用のトレースフラグ `HAKMEM_TINY_DEBUG_FAST0=1` で front 層を最小順序に固定(Hot/TLS/Mag を飛ばして直接 remote → TLS リストへ流す) - [ ] フラグ ON/OFF で crash が消えるか確認 → front 内バグか remote 内バグかを切り分け 3. **再現テスト・フラグ** - [ ] `scripts/run_larson_defaults.sh tput` に `HAKMEM_TINY_FAST_CAP` オーバーライドを追加(忘れ防止) - [ ] `scripts/` に crash 再現スクリプト `run_larson_fast0.sh` を用意(2s/4T で SIGSEGV を取得) 4. **publish pipeline 調査(二次優先)** - crash 解消後に `tiny_publish_notify` の発火率と mailbox drain を再計測し、mmap 偏重の根本原因へ戻る ### 参考コマンド - Crash 再現: `HAKMEM_TINY_FAST_CAP=0 HAKMEM_LARSON_TINY_ONLY=1 HAKMEM_TINY_USE_SUPERSLAB=1 ./larson_hakmem 2 8 128 1024 1 12345 4` - Debug Ring ダンプ: `HAKMEM_TINY_TRACE_RING=1 ... ./larson_hakmem ...` `kill -USR2 ` で途中ダンプ、`SIGSEGV` で最終ダンプ - publish 通電検証(安全ラン): `scripts/run_larson_defaults.sh tput 2 4` 現状整理(Google系/Larson系ベンチの追い上げフェーズ) ### 実施済み(即応) - ベンチ実行時のデバッグ出力によるオーバーヘッドを除去(リリース既定で抑制) - 変更ファイル: `core/hakmem_tiny_ultra_simple.inc`, `core/hakmem_tiny_metadata.inc` - 方針: `HAKMEM_DEBUG_VERBOSE` が有効時のみ `fprintf` するようガード - 効果: ログ出力がボトルネックになるケースを解消(特に tiny/mixed ベンチ) - `bench_random_mixed_hakmem` を ULTRA_SIMPLE 版で再ビルド・再計測 - ビルド: `make bench_random_mixed_hakmem EXTRA_CFLAGS="-DHAKMEM_TINY_PHASE6_ULTRA_SIMPLE=1 -DHAKMEM_BUILD_RELEASE=1"` - 旧: 23.49 M ops/sec → 新: 25.82 M ops/sec(+9.9%) - Larson ベンチ(2秒, 8–128B, chunks=1024, rounds=1, seed=12345)実行 - system 1T: 14.73 M/s, 4T: 16.76 M/s - mimalloc 1T: 16.77 M/s, 4T: 16.77 M/s - HAKMEM 1T: 2.52 M/s, 4T: 4.19 M/s - `HAKMEM_LARSON_TINY_ONLY=1 HAKMEM_DISABLE_BATCH=1` でも同等(~2.56M / ~4.19M) ### 観測と仮説(Larson 遅さの主因) - 既知の分析と一致: 再利用不足 → ページフォールト/ゼロ化増 → sys 時間が支配的 - Tiny フロントのヒット率が不足(SLL を使うが、Larson パターンで十分に温まらない) - Metadata 版(Phase 6-1.6)は refill 未対応部分があり現状は封印、ULTRA_SIMPLE で進めるのが安全 ### 当面の方針(追いつくまでの短期プラン) 1) Larson 用バイナリに ULTRA_SIMPLE を徹底適用してフロントのヒット率を最大化 - 目標: free/alloc ともに 3–4 命令の経路に乗せる(既に `free()` は alignment-guess 経路有効) - ビルド: `EXTRA_CFLAGS="-DHAKMEM_TINY_PHASE6_ULTRA_SIMPLE=1 -DHAKMEM_BUILD_RELEASE=1"` 2) Mixed/Larson の再測定と perf 取得 - コマンド: `scripts/run_larson.sh -d 2 -t 1,4` - 詳細: `scripts/run_larson_perf.sh`(PF/IPC/branch/L1d を併記) 3) 迅速チューニング候補(小粒で効く順) - Refill 個数の抑制(64→16〜32)で温まり時間短縮、TL-再利用密度を上げ PF を減らす - SuperSlab サイズの下限を 1MB に固定(`HAKMEM_TINY_SS_MIN_MB=1`)で初期PFを軽減 - 事前ウォーム(Larson開始前に `sll_refill_small_from_ss()` をサイズ帯毎に数回) - size→class 変換の LUT 確認(既に O(1) だが、統合経路でも分岐予測を安定化) 4) 中期(必要なら) - Dual Free Lists(local/remote 分離)を Tiny に導入(既存の設計を Tiny へ移植) - Metadata 版の refill 実装を完了(ヘッダ +8B で owner 判定ゼロ化)し安定化後に切替検討 ### 直近 TODO(実行順) - [ ] Larson ULTRA_SIMPLE ビルドの固定化(larson_hakmem に EXTRA_CFLAGS 反映) - [ ] `scripts/run_larson.sh -d 2 -t 1,4` の再実行と結果更新 - [ ] `scripts/run_larson_perf.sh` で PF/CPU 内訳を取得してボトルネックの再確認 - [ ] Refill 個数/SSサイズのチューニングで 1T: ~10M, 4T: ~10M の域まで引き上げ 備考: この更新で、測定時のノイズ(fprintf)は排除済み。以降の差分は純粋にアルゴリズム/チューニング起因として評価可能。 --- ## 🔧 バグ修正と3点セット実装(2025-11-03 09:00) 結論: 「free 経路の破綻」は修正済み。OOM は設計的な再利用探索の不足が残課題。 - 修正 - ULTRA_SIMPLE free を same-thread のみ直 push に制限。cross-thread free は従来経路へフォールバック。 - 変更: `core/hakmem.c:820`, `core/hakmem_tiny_ultra_simple.inc:96` - OOM ワンショット診断(errno/ss_size/alloc_size/RLIMIT/VmSize/RSS/SSカウンタ)を追加。 - 変更: `core/hakmem_tiny_superslab.c:182` - 3点セット(段階導入・既定OFF) 1) remote queue(cross-thread free 時に per-slab MPSC stack へ) 2) partial publish/adopt(クラス別公開リング→refill 前に adopt) 3) adopt 時の remote drain + owner 移譲(best-effort) - 変更: `core/hakmem_tiny_superslab.h`, `core/hakmem_tiny.c`, `core/hakmem_tiny_free.inc` - 有効化: `HAKMEM_TINY_SS_ADOPT=1` - 観測(Larson, 4T) - adopt OFF(既定): ~4.19 M/s 安定、稀に ENOMEM 継続 - adopt ON: OOM は減るがゼロにはならず。1T は低下傾向(~2.3–2.4 M/s)→チューニング要 - 次のチューニング(提案) - `SS_PARTIAL_RING` 長(2/4/8)A/B、adopt 選好(remote多い slab 優先)、採用頻度の抑制(クールダウン) - perf stat(PF/DTLB)の比較で改善度を定量化 使い方(A/B): ``` # 既定(adopt OFF) ./larson_hakmem 2 8 128 1024 1 12345 4 # adopt ON(3点セット有効、A/B計測) HAKMEM_TINY_SS_ADOPT=1 ./larson_hakmem 2 8 128 1024 1 12345 4 ``` ## 🎯 現在のミッション: Phase 6 - Learning-Based Tiny Allocator **Status**: ✅ **Phase 6-1 完了!** 🚀🎉 **最新アップデート (2025-11-02 18:00):** - ✅ **Phase 6-1: Ultra-Simple Fast Path 完了!** 🚀🚀🚀 - **驚異的な結果**: **478.60 M ops/sec** (64B LIFO) - **System malloc の +174% 高速!** (174.69 M/s → 478.60 M/s) - **現行 HAKMEM の +777% 高速!** (54.56 M/s → 478.60 M/s) - **4.17 cycles/op** (理論的最小値に近い) - **100% hit rate** (10M ops 中 miss 1回のみ) - **実装**: "Simple Front + Smart Back" (HAKX Mid-Large +171% の成功パターン適用) - Fast path: 3-4 命令 (tcache風 pop from free list) - Backend: Simple mmap-based chunk allocator - Files: `core/hakmem_tiny_simple.{h,c}` (200行) - **なぜこんなに速い?** 1. Ultra-simple fast path (分岐予測完璧) 2. Perfect L1 cache locality (TLS array 64B) 3. Freed blocks 即再利用 (LIFO で 100% hit) 4. ゼロオーバーヘッド (magazine layers なし) - **次のステップ**: - [ ] Comprehensive benchmark (21 patterns) - [ ] Memory efficiency 測定 - [ ] Phase 2: Learning layer 設計 --- ## 📋 過去の試行 (Phase 5以前) **Status (旧)**: ✅ Phase 2+1完了 → ❌ Phase 3失敗 → ❌ Phase 4-A1失敗 → ❌ **Phase 5-A失敗** → ❌ **Phase 5-B-Simple 失敗** 💥 **最新アップデート (2025-11-02 07:00):** - ❌ **Phase 5-B-Simple 失敗**: **-71% (ST) / -35% (MT)** 💥💥💥 - **Single-threaded (bench_tiny_hot, 64B)**: - System: 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, 8-32KB, 2 threads)**: - System: 11.51 M ops/sec - HAKMEM Phase 5-B: 7.44 M ops/sec - **Regression: -35%** (1.5x slower) - ⚠️ **NOTE**: Mid/large benchmark tests 8-32KB allocations (outside Tiny range), not directly testing Phase 5-B - **根本原因分析**: 1. **Magazine capacity ミスチューン**: 64 slots は ST workload には小さすぎる - Batch=100 の場合、2回に1回は slow path に落ちる - System allocator は tcache (7+ entries per size) で高速 2. **Migration logic オーバーヘッド**: Slow path での free list → Magazine migration が高コスト - Batch migration (32 items) が頻繁に発生 - Pointer chase + atomic operations 3. **Dual Free Lists の誤算**: ST では効果ゼロ、むしろオーバーヘッド - ST では remote_free は発生しない - Dual structures のメモリ overhead のみ 4. **Unified Magazine の問題**: 統合で simplicity は得たが performance は失った - 旧 HotMag (128 slots) + Fast + Quick の組み合わせのほうが高速だった - 単純化 ≠ 高速化 - **教訓**: - ✅ **Magazine unification 自体は良アイデア** (complexity 削減) - ❌ **Capacity tuning が不適切** (64 slots → 128+ 必要) - ❌ **Dual Free Lists は MT 専用** (ST で導入すべきでない) - ❌ **Migration logic が重すぎる** (batch size 削減 or lazy migration 必要) - **次のアクション**: 1. ⏮️ **Phase 5-B をロールバック** (git revert) 2. 📊 **Baseline 再測定** (clean state で確認) 3. 🤔 **Phase 5-B-v2 を検討** (Magazine unification only, Dual Free Lists なし) 4. 🎯 **Alternative approach**: Phase 6 系 (L25/SuperSlab 最適化) に移行 - 🚀 **Phase 5-B-Simple 開始** (2025-11-03 04:00): Dual Free Lists + Magazine統合 🎯 - **Phase 5-A-v2 をスキップする理由**: 1. **HAKMEM は既に O(1) size→class 変換** (`g_size_to_class_lut_1k[size]`) 2. **多層 cache が既に 95%+ hit** → direct cache 追加は効果薄い 3. **TLS 変数追加で cache pollution** リスク 4. **期待値**: ±0% (効果とオーバーヘッドが相殺) - **Phase 5-B-Simple に集中** (期待: +15-23%) - **2つの最適化を統合**: 1. **Dual Free Lists** (mimalloc Phase 5-B): +10-15% - Local free で atomic 不要 (10+ cycles 削減) - Cache locality 向上 2. **Magazine 統合** (シンプル化): +3-5% - 3-4層 → 2層に削減 - TLS cache line を 3-4本 → 1-2本に削減 - Branch を 3-4回 → 1回に削減 - **実装計画** (3-4 days): ```c // === Before (現状: 複雑) === // Layer 1: HotMag (classes 0-2) if (g_hotmag_enable) ptr = hotmag_pop(); // Layer 2: Hot functions (classes 0-3) if (g_hot_alloc_fn[cls]) ptr = tiny_hot_pop_classN(); // Layer 3: Fast cache ptr = tiny_fast_pop(cls); // Layer 4+: Slow path (single freelist + atomic) ptr = hak_tiny_alloc_slow(); // === After (Phase 5-B-Simple: シンプル) === // Layer 1: Unified Magazine (統合版) TinyUnifiedMag* mag = &g_tls_mag[cls]; if (mag->top > 0) return mag->items[--mag->top]; // ← 1 branch! // Layer 2: Slow path (Dual Free Lists) return tiny_alloc_slow_dual(cls); // ← local_free (no atomic!) ``` - **Dual Free Lists の核心**: ```c typedef struct TinySlab { // Phase 5-B: Dual Free Lists void* local_free; // Local frees (no atomic!) _Atomic(void*) thread_free; // Remote frees (atomic) } TinySlab; // Free: Local は atomic なし! if (pthread_equal(slab->owner_tid, self)) { *(void**)ptr = slab->local_free; slab->local_free = ptr; // ← No atomic! 10+ cycles 削減 } else { atomic_push(&slab->thread_free, ptr); } // Migration: Batch で効率化 if (!slab->free && slab->local_free) { slab->free = slab->local_free; // Pointer swap only slab->local_free = NULL; } ``` - **期待効果まとめ**: | 最適化 | 期待効果 | 累積 M ops/sec | |--------|---------|----------------| | Baseline | - | 16.53 | | Dual Free Lists | +10-15% | 18.2-19.0 | | Magazine 統合 | +3-5% | 18.7-20.0 | | Branch 削減 | +2-3% | 19.1-20.6 | | **合計** | **+15-23%** | **19.1-20.3 M ops/sec** 🎯 | - **実装ステップ**: - **Step 1** (Day 1): Unified Magazine 実装 & benchmark - **Step 2** (Day 2): Dual Free Lists 追加 (TinySlab 構造変更) - **Step 3** (Day 3): Free path 書き換え (local_free / thread_free 分離) - **Step 4** (Day 4): Migration logic & 最終 benchmark - **削減される TLS 変数**: ```c // Before (~1600 bytes + flags) __thread TinyHotMag g_tls_hot_mag[8]; // 1024 bytes __thread void* g_fast_head[8]; // 64 bytes __thread uint16_t g_fast_count[8]; // 16 bytes __thread TinyQuickSlot g_tls_quick[8]; // 512 bytes // After (2048 bytes のみ) __thread TinyUnifiedMag g_tls_mag[8]; // 2048 bytes ``` - ❌ **Phase 5-A失敗**: **-3~-7.7%** (16.53 → 15.25-16.04 M ops/sec) 💥 - **実装**: Global `slabs_direct[129]` でO(1) direct page cache - **結果**: 性能**大幅悪化**(期待+15-20% → 実際-3~-7.7%) - **根本原因**: **Thread-local vs Global の設計ミス** ```c // hakmem_tiny.h - 間違った実装 typedef struct { TinySlab* slabs_direct[129]; // ❌ Global = 全threadが共有 } TinyPool; // g_tiny_pool は global // mimalloc - 正しい実装 typedef struct mi_heap_s { mi_page_t* pages_free_direct[129]; // ✅ Heap ごと = thread-local } mi_heap_t; // __thread mi_heap_t* heap ``` - **性能悪化の3つの要因**: 1. **pthread_self() オーバーヘッド**: 毎回 `tiny_self_pt()` syscall (~20-40 cycles) 2. **Remote slab hit**: Global cache が他 thread の slab を指す → Owner check → Cache clear → Fallback (無駄な3段階処理) 3. **余分な分岐**: 既存 fast path の前に新たな条件分岐層を追加 - **測定結果** (2回計測): - Run 1: 15.25 M ops/sec (-7.7%) - Run 2: 16.04 M ops/sec (-3.0%) - Baseline: 16.53 M ops/sec - **教訓**: **mimalloc の直訳は危険** - アーキテクチャ差異を理解すべき - mimalloc: `mi_heap_t` は thread-local → `pages_free_direct` も thread-local - HAKMEM: `TinyPool g_tiny_pool` は global → `slabs_direct` は全 thread 共有 - **Revert**: 全ての Phase 5-A 変更を完全に revert (16.33 M ops/sec に復元) - **次の戦略**: Phase 5-A-v2 で **thread-local slabs_direct** を実装 ```c // 次回の正しい実装方針 __thread TinySlab* g_tls_slabs_direct[129]; // ✅ Thread-local ``` - 🎊 **Phase 5完了: 47% Gap の正体を解明!** 🔍 - **mimalloc 完全分析**: 10,000語超の詳細レポート作成完了 - **3つのファイル生成**: - `MIMALLOC_ANALYSIS_REPORT.md` - 詳細技術分析 - `MIMALLOC_KEY_FINDINGS.md` - 要約版 - `MIMALLOC_IMPLEMENTATION_ROADMAP.md` - 実装計画 - **Gap の内訳判明**: 1. **Direct Page Cache (O(1))**: +15-20% ← **最大のボトルネック!** 2. **Dual Free Lists**: +10-15% 3. **Branch Hints + Lazy Updates**: +5-8% - **重要な発見**: mimalloc も **intrusive linked list** を使用 → Phase 3/4-A1 の「linked-list は最適」は**正しかった**! → Gap は**マイクロ最適化**から来る(データ構造選択ではない) - **Hot Path サイクル比較**: - mimalloc: ~20 cycles (TLS 2 + O(1) lookup 3 + Pop 5) - HAKMEM: ~30-35 cycles (TLS 3 + Binary search 5 + Atomic pop 15) - **差分: 10-15 cycles → 47% の性能差** - 📋 **次のアクション**: Phase 5-A 実装(Direct Page Cache) - **期待効果**: +15-20% (16.53 → 19.0-19.8 M ops/sec) - **Effort**: 1-2 days - **Risk**: Low - ❌ **Phase 4-A1失敗**: **-0.24%** (16.53 → 16.49 M ops/sec) 💥 - 実装: TLS-BUMP即値化(immediate-value hot functions) - 結果: 性能**悪化**(期待+5-8% → 実際-0.24%) - 原因: **TLS-BUMPは mixed workloadで機能しない** ```c // hakmem_tiny_refill.inc.h:258 if (meta->freelist != NULL) return NULL; // linear mode only ``` - 根本問題: bench_random_mixed (50% alloc, 50% free) では freelist が常に populated - **TLS-BUMPは monotonic allocationのみ有効**(連続allocのみ) - 教訓: **混在ワークロードでは linked-list が最適** - 追加層は純粋なオーバーヘッド - 🚀 **Phase 4戦略決定** (2025-11-02 22:00) - ChatGPT Pro相談完了:構造的アプローチで+10-25%を狙う - 核心: 現在の6-7層を**3層に統合**(層あたり2-3ns削減) - 即効施策: TLS-BUMP即値化(+5-8%期待) ← **失敗!** - 中期施策: 小マガジン128化(+3-5%)+ 3層リファクタ(+10-15%) - 長期施策: Mid/Large構造改革(per-core arena + TL-Segment) - ❌ **Phase 3失敗教訓**: **+0.24% のみ** (16.53 → 16.57 M ops/sec) - 実装: v3 allocator (magazine-based single-tier) - 原因: 既存のlinked-listが既にmagazineより最適 - 教訓: **Linked list > Magazine array**(メモリアクセス少ない) - ✅ **Phase 2+1完了**: **+1.8% 改善** (16.24 → 16.53 M ops/sec) - Phase 2: TLS range check実装 (owner_slab高速化) - Phase 1: free()順序変更 (Tiny → Mid MT) - 結果: 理論通り動作、軽微な改善 ✅ - 📋 **次のアクション**: Phase 4-A1実装開始(TLS-BUMP即値化) **Phase 2+1の教訓:** 1. ✅ TLS range check + 順序変更は**理論通り動作**(50% overhead削減) 2. ❌ free() overheadが想定より小さかった(実測 ~8% vs 想定 ~21%) 3. 💡 **シングルスレッドでは mutex overhead は誤差レベル** 4. 🎯 さらなる改善には**malloc/alloc側**を攻めるべき(27% overhead!) **コスト分析(Phase 2+1):** ``` Before (Mid → Tiny): Mid null checks: 4 cycles Mid mutex (empty): 15 cycles ← 想定より軽い! Tiny registry: 15 cycles Total: 34 cycles After (Tiny → Mid): TLS check: 2 cycles Tiny registry: 15 cycles Total: 17 cycles Savings: 17 cycles (50% 削減) 全体への影響: 17 cycles × (free overhead 8%) ≈ +2% 🎯 ``` --- ## 📊 最新perf分析結果 (2025-11-01) ### ベンチマーク条件 - **ワークロード**: bench_random_mixed (8-128B, 16 size classes) - **パラメータ**: 200K cycles, 400 ws, seed=1 - **スレッド**: 1 (シングルスレッド) ### パフォーマンス比較 | Allocator | Throughput | vs mimalloc | |-----------|------------|-------------| | HAKMEM | 16.46 M ops/sec | 68% | | mimalloc | 24.21 M ops/sec | 100% | **Gap: 32% slower** ⚠️ ### 根本原因: アロケータオーバーヘッド | Allocator | Total Overhead | malloc/alloc | free/delete | |-----------|----------------|--------------|-------------| | mimalloc | 17% | 7.35% | 9.77% | | HAKMEM | **49%** | 27% | 21.64% | **HAKMEM は 3x のCPUサイクルを消費!** --- ## 🔴 Critical Bottlenecks (優先度順) ### 1. `free()` の無駄なMid Range チェック (Priority 1) 🔥 **問題**: ``` free(ptr) ↓ Lock g_mid_registry mutex (2.29%) ← 全freeでロック! ↓ Binary search g_mid_registry (7.08%) ← 8KB-32KB範囲チェック ↓ Unlock mutex (3.93%) ↓ hak_tiny_owner_slab (4.98%) ← 8B-1KB範囲チェック (本来これだけでOK!) ↓ hak_tiny_free ← 実際のfree ``` **無駄**: 8-128Bのワークロードなのに、全freeでMid Range (8KB-32KB) をチェック! - mutex lock/unlock: 6.22% - mid_lookup: 7.08% - **合計 13.3% のオーバーヘッド** が不要 **修正方針**: ```c free(ptr) ↓ Fast TLS range check (Tiny: 8B-1KB) ← NO MUTEX, 直接チェック ↓ (hit: ~90% for this workload) hak_tiny_free ↓ (miss) Check Mid registry (with mutex) ↓ (miss) Check L25/other ``` **期待効果**: **~13% スループット向上** (16.46 → 18.6 M ops/sec) --- ### 2. `hak_tiny_owner_slab` の線形探索 (Priority 2) 🟡 **問題**: - 現在: TLS slab listを線形探索 (3.50% overhead) - mimalloc: ポインタビットパターンで高速判定 **修正方針** (mimalloc-style): ```c // 1. Alignment check if ((ptr & (SLAB_SIZE - 1)) != 0) return NULL; // Not slab-aligned // 2. TLS range check if (ptr < tls_heap_start || ptr >= tls_heap_end) return NULL; // 3. Direct slab header access SlabHeader* slab = (SlabHeader*)(ptr & ~(SLAB_SIZE - 1)); return slab; ``` **期待効果**: **~3% スループット向上** (3.50% → ~0.5%) --- ### 3. `hak_tiny_alloc_slow` の複雑なフォールバック (Priority 3) 🟢 **問題**: - 4段階フォールバック: hotmag → TLS list → superslab → magazine - 各段階でTLS変数アクセス + 分岐 **修正方針**: ```c // 1. TLS magazine (most common) if (fast cache has space) return pop(); // 2. Superslab (if enabled) if (g_use_superslab && superslab_active) return superslab_alloc(); // 3. Central refill return refill_from_central(); ``` **期待効果**: **~2-3% スループット向上** --- ### 4. 関数インライン化 (Priority 4) 🟢 **候補**: - `hak_tiny_alloc` → `malloc` にインライン - `hak_tiny_owner_slab` → `free` にインライン **期待効果**: **~2% スループット向上** --- ## 🎨 Phase 2詳細設計: mimalloc-style Fast Owner Check ### 現在の問題点 **現在の `hak_tiny_owner_slab()` 実装:** ```c TinySlab* hak_tiny_owner_slab(void* ptr) { int hash = registry_hash(slab_base); // 関数呼び出し + hash計算 for (int i = 0; i < SLAB_REGISTRY_MAX_PROBE; i++) // 最大8回ループ // array access + 比較 + atomic load if (ptr < start || ptr >= end) return NULL; // range validation } ``` **コスト分析:** - **Positive lookup** (Tiny allocation): hash + 1-2 probes + range check = **~10-15 cycles** - **Negative lookup** (非Tiny): hash + 1-2 probes = **~8-10 cycles** ← Phase 1失敗の原因! ### Phase 2新設計: Ultra-Fast Owner Check **目標:** - **Negative lookup**: **1-2 cycles** (現状: 8-10 cycles) → **~85% 削減** 🎯 - **Positive lookup**: **5-8 cycles** (現状: 10-15 cycles) → **~40% 削減** **実装戦略:** ```c // Phase 2: Ultra-fast owner check (mimalloc-style) static inline TinySlab* hak_tiny_owner_slab_fast(void* ptr) { // Step 1: TLS heap range check (1-2 cycles) ← KEY OPTIMIZATION // Check if ptr is in this thread's Tiny heap range if (ptr < g_tls_tiny_min || ptr >= g_tls_tiny_max) { return NULL; // Outside TLS range → FAST NEGATIVE LOOKUP! ✅ } // Step 2: Slab base calculation (1 cycle) uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1); // Step 3: Registry lookup (2-3 cycles) // Now only called for pointers IN TLS range (hit rate ~90%) TinySlab* slab = registry_lookup(slab_base); if (!slab) return NULL; // Step 4: Range validation (1-2 cycles) if (ptr < slab->base || ptr >= slab->base + TINY_SLAB_SIZE) { return NULL; } return slab; } ``` **最適化ポイント:** 1. **TLS range check を最初に実行** → Negative lookup を 1-2 cycles に! 2. Registry lookup は TLS range内のみ実行 → Hit rate ~90% 3. 既存のregistry_lookupを活用 → 安全性維持 ### 実装要件 **1. TLS heap range tracking:** ```c // hakmem_tiny.h extern __thread void* g_tls_tiny_min; extern __thread void* g_tls_tiny_max; // hakmem_tiny.c __thread void* g_tls_tiny_min = (void*)UINTPTR_MAX; __thread void* g_tls_tiny_max = NULL; // Update on slab allocation (in allocate_new_slab) static inline void update_tls_tiny_range(void* slab_base) { if (slab_base < g_tls_tiny_min) g_tls_tiny_min = slab_base; void* slab_end = slab_base + TINY_SLAB_SIZE; if (slab_end > g_tls_tiny_max) g_tls_tiny_max = slab_end; } ``` **2. 既存コードとの互換性:** - `hak_tiny_owner_slab()` をfast版に置き換え - Registry lookup機構はそのまま維持 (thread-safe) - SuperSlabは別途処理(既存通り) ### 期待効果 **Negative lookup高速化 (非Tiny allocations):** - Before: hash (2-3 cycles) + probe (3-4 cycles) + compare = 8-10 cycles - After: range check (1-2 cycles) = **~85% 削減** 🔥 **Positive lookup高速化 (Tiny allocations):** - Before: hash + probe + range = 10-15 cycles - After: range + registry + range = 5-8 cycles = **~40% 削減** **Combined with Phase 1 (順序変更):** ``` Tiny allocations (90%): Before: Mid mutex (13.6%) + owner_slab (8-10 cycles) After: owner_slab_fast (1-2 cycles) → Tiny free Savings: 13.6% × 0.9 = ~12% 🎯 Mid MT allocations (10%): Before: owner_slab (8-10 cycles) + Mid mutex After: owner_slab_fast (1-2 cycles) + Mid mutex Savings: 8 cycles × 0.1 = ~0.5% Total expected gain: ~12-13% (16.46 → 18.4-18.6 M ops/sec) ``` --- ## 📋 実装タスクリスト ### ✅ Phase 1: free() チェック順序変更 - **完了 (revert含む)** ✅ **実装完了:** - ✅ 1-1. `hakmem.c` free()のチェック順序を変更 (Tiny→Mid) - ✅ 1-2. ベンチマーク測定: 16.34 M ops/sec (**-0.73% regression**) - ✅ 1-3. perf再測定して原因分析: `hak_tiny_owner_slab()` が重い (8-10 cycles) - ✅ 1-4. Phase 1を revert: 16.24 M ops/sec (baseline復帰) **教訓:** `hak_tiny_owner_slab()` が重すぎて全freeに適用不可 → Phase 2で根本解決 --- ### 🚀 Phase 2: owner_slab 高速化 - **実装中** 🚀 **目標:** - Negative lookup: 8-10 cycles → **1-2 cycles** (~85%削減) - Positive lookup: 10-15 cycles → **5-8 cycles** (~40%削減) - Phase 2完了後、Phase 1再適用 → **~12-13%向上** **タスクリスト:** - [ ] 2-1. TLS range tracking変数追加 - [ ] `hakmem_tiny.h`: extern宣言追加 - [ ] `hakmem_tiny.c`: TLS変数定義 - [ ] `allocate_new_slab()`: range更新ロジック追加 - [ ] 2-2. `hak_tiny_owner_slab_fast()` 実装 - [ ] TLS range check (negative lookup高速化) - [ ] Registry lookup (既存機構活用) - [ ] Range validation (safety確保) - [ ] 2-3. 既存 `hak_tiny_owner_slab()` を置き換え - [ ] 関数名変更 or 実装差し替え - [ ] 全呼び出し箇所で動作確認 - [ ] 2-4. ベンチマーク測定 - [ ] `./bench_random_mixed_hakmem 200000 400 1` - [ ] 目標: ~16.24 M ops/sec (変化なし or 微増) - [ ] 理由: owner_slab単体では効果小、Phase 1再適用で効果発揮 - [ ] 2-5. Phase 1再適用 (順序変更) - [ ] `hakmem.c` free(): Tiny → Mid に変更 - [ ] ベンチマーク測定 - [ ] 目標: **18.4-18.6 M ops/sec (+12-13%)** 🎯 ### 🎯 Phase 3: Allocation Hot Path簡素化 - **設計完了** 📐 **Status**: Phase 2+1完了 → Phase 3設計完了 (2025-11-01 22:00) **目標**: malloc/allocのoverhead削減 (27% → 20%) - **期待効果**: +5-10% (16.53 → 17.5-18.0 M ops/sec) - **アプローチ**: mimalloc-style single-tier hot path **現状分析 (perf):** ``` malloc/alloc overhead: 27% - hak_tiny_alloc_slow: 9.33% (複雑なフォールバック) - hak_tiny_alloc: 7.12% (hot path) - malloc wrapper: 3.67% - その他: ~7% 問題点: ❌ 6+段階のフォールバックチェーン ❌ 多数のTLS変数アクセス ❌ Heavy stack frame (14.05% in prologue!) ❌ Branch misprediction ``` **現在のフローチャート:** ```c hak_tiny_alloc(size) ↓ 1. size → class_idx ↓ 2. ifdef BENCH_FASTPATH: - SLL head check - TLS Magazine check - SLL refill ↓ 3. HotMag front (class <= 2) ↓ 4. Hot alloc functions (class 0-3) ↓ 5. tiny_fast_pop() ↓ 6. hak_tiny_alloc_slow() ← 9.33% overhead! - HotMag refill - TLS list refill - SuperSlab fallback ``` **Phase 3新設計 (mimalloc-style):** ```c void* hak_tiny_alloc_v3(size_t size) { // 1. Size → class (branchless) int class_idx = hak_tiny_size_to_class(size); if (__builtin_expect(class_idx < 0, 0)) return NULL; // 2. Single-tier TLS magazine (HOT PATH - 2-3 cycles) TinyTLSMag* mag = &g_tls_mags[class_idx]; int top = mag->top; if (__builtin_expect(top > 0, 1)) { void* ptr = mag->items[--top].ptr; mag->top = top; return ptr; // ← 最速パス! 🚀 } // 3. Refill + fallback (cold path) return hak_tiny_alloc_slow_v3(size, class_idx); } static void* __attribute__((cold, noinline)) hak_tiny_alloc_slow_v3(size_t size, int class_idx) { TinyTLSMag* mag = &g_tls_mags[class_idx]; // Step 1: Try refilling magazine from SuperSlab if (mag_refill_from_superslab(class_idx, mag, 32) > 0) { return mag->items[--mag->top].ptr; } // Step 2: Allocate new SuperSlab return hak_tiny_alloc_superslab(class_idx); } ``` **削減内容:** ``` Branches: 6+ → 2 TLS変数: 多数 → 1つ (mag) Stack frame: Heavy → Minimal (inline候補) Hot path cycles: ~20-30 → ~5-8 (60-70%削減) 🎯 ``` **タスクリスト:** - [ ] 3-1. `hak_tiny_alloc_v3()` 実装 - [ ] Single-tier magazine hot path - [ ] Branchless size-to-class - [ ] Minimal stack frame - [ ] 3-2. `hak_tiny_alloc_slow_v3()` 簡素化 - [ ] 2-tier fallback (magazine refill → superslab) - [ ] Remove HotMag/TLS list/fast_pop complexity - [ ] `__attribute__((cold, noinline))` - [ ] 3-3. Magazine refill最適化 - [ ] `mag_refill_from_superslab()` 専用関数 - [ ] Batch refill (32-64 items) - [ ] Zero overhead on hit - [ ] 3-4. ベンチマーク測定 - [ ] `./bench_random_mixed_hakmem 200000 400 1` - [ ] 目標: **17.5-18.0 M ops/sec (+5-10%)** 🎯 - [ ] vs mimalloc: 72-74% --- ### Phase 4: 関数インライン化 (Phase 3成功後) **Phase 4: インライン化** - `hak_tiny_alloc_v3` → `malloc` (force inline) - `hak_tiny_free` → `free` (force inline) - 目標: +2-3% (18.0-19.0 M ops/sec) --- ## 🎯 マイルストーン | Phase | Target Throughput | Actual Result | vs mimalloc | Status | |-------|-------------------|---------------|-------------|--------| | Baseline | 16.24 M ops/sec | 16.24 M ops/sec | 67% | ✅ Baseline | | ~~Phase 1 (単体)~~ | ~~18.6 M ops/sec~~ | ~~16.34 M ops/sec~~ | ~~67%~~ | ❌ **FAILED** (-0.73%) | | Phase 2 (単体) | ~16.2 M ops/sec | 15.70 M ops/sec | 65% | ✅ **完了** (-3.3%, 予想通り) | | **Phase 2+1 Combined** | ~~18.4-18.6 M ops/sec~~ | **16.53 M ops/sec** | **68%** | ✅ **完了** (+1.8%) | | ~~Phase 3 (v3 alloc)~~ | ~~17.5-18.0 M ops/sec~~ | ~~16.57 M ops/sec~~ | ~~68%~~ | ❌ **FAILED** (+0.24%) | | Phase 4 (インライン化) | 18.0-19.0 M ops/sec | - | 74-79% | ⏳ TODO | | **Ultimate Goal** | **22-24 M ops/sec** | - | **90-100%** | 🌟 Long-term Target | **Phase 2+1の洞察 (2025-11-01 21:30):** - ✅ Phase 2+1は**理論通り動作** (+1.8% 改善) - ❌ 期待値(+12-13%)との乖離は**Mid MT mutexコスト見積もり誤り**が原因 - 💡 シングルスレッド、空registryでは mutex は超軽量(~15 cycles) - 🎯 **次の主戦場**: malloc/alloc側の27% overheadを攻める! --- ## 📁 関連ドキュメント - ✅ **perf分析レポート**: `docs/PERF_ANALYSIS_TINY_MIXED.md` - 詳細なボトルネック分析 - perf annotate結果 - 最適化ロードマップ - 📊 **ベンチマーク比較**: 他のAIちゃんのレビュー - Mixed: HAKMEM 66.7% of mimalloc (weak) - Mid MT: HAKMEM 129.4% of mimalloc (strong) --- ## 💡 Next Action (今すぐやること) ### 最初のステップ: free() Fast TLS Check実装 ```bash # 1. hakmem.c のfree()を開いて、Fast TLS checkを追加 vim core/hakmem.c # 2. 変更箇所 # free() の冒頭 (guard check後) に: # - TLS Tiny heap範囲チェック # - Hit時は直接 hak_tiny_free() # - Miss時は既存フロー # 3. ビルド & ベンチマーク make bench_random_mixed_hakmem ./bench_random_mixed_hakmem 200000 400 1 # 4. perf確認 perf record -F 999 -g ./bench_random_mixed_hakmem 200000 400 1 perf report --stdio -n --percent-limit 1 ``` --- ## 🔥 モチベーション **目標**: Tiny Mixed Workloadで mimalloc に匹敵する性能 **現状**: - Mid MT: **138% of mimalloc** ✅ (Already winning!) - Tiny Mixed: **68% of mimalloc** ⚠️ (Need improvement!) **今日の目標**: - Phase 1実装で **77% of mimalloc** (16.46 → 18.6 M ops/sec) **Let's optimize! 🚀** --- ## 🚀 Phase 4: 構造的最適化戦略(2025-11-02策定) ### 戦略概要 **目標**: mimalloc比90-100%到達(現状68% → 22-24 M ops/sec) **核心アプローチ**: 1. **3層への統合**(現在の6-7層を削減) 2. **TLS-BUMP即値化**(hot-class専用最適化) 3. **段階的実装**(即効 → 中期 → 長期) ### Phase 4-A: 即効施策(今週実装) #### A-1: TLS-BUMP即値化 + hot-class強化 ⚡ **期待効果**: +5-8%(16.53 → 17.5-18.0 M ops/sec) **実装内容**: - `g_ultra_bump_shadow`有効化(現在無効) - hot-class(16/32/64B)を即値化関数に書き換え - 分岐ゼロ化(cmov版も検討) **変更ファイル**: - `core/hakmem_tiny_hot_pop.inc.h` - 即値化版関数 - `core/hakmem_tiny.c` - BUMP有効化 - `core/hakmem_tiny_config.c` - デフォルト設定 **コード例**: ```c // Before (現在) void* head = g_fast_head[class_idx]; if (head) { g_fast_head[class_idx] = *(void**)head; return head; } // After (A-1) uint8_t* p = g_tls_bcur[0]; uint8_t* n = p + 16; // ← 即値! if (likely(n <= g_tls_bend[0])) { g_tls_bcur[0] = n; return p; // ← 分岐1つ、TLS書き込み1回 } return tiny_bump_refill_cold(0); // noinline ``` #### A-2: 小マガジン容量最適化 📦 **期待効果**: +3-5% **実装内容**: - class 0-2(8/16/32B): 128エントリ固定 - class 3+(64B+): 64エントリ(現状維持) - L1ヒット率向上、ワーキングセット最適化 **変更ファイル**: - `core/hakmem_tiny_magazine.h` - 容量定数 ### Phase 4-B: 中期施策(1-2週間) #### B-1: 3層への統合リファクタリング 🏗️ **期待効果**: +10-15%(累積+20-25%) **実装内容**: ```c void* hak_tiny_alloc_v4(size_t size) { int k = hak_tiny_size_to_class(size); if (unlikely(k < 0)) return NULL; // Layer 1: TLS-BUMP (hot-class専用、即値化) if (g_hot_alloc_fn[k]) { void* p = g_hot_alloc_fn[k](); if (likely(p)) return p; } // Layer 2: 小マガジン128 TinyTLSMag* mag = &g_tls_mags[k]; if (likely(mag->top > 0)) { return mag->items[--mag->top].ptr; } // Layer 3: Slow (全部noinline/cold) return hak_tiny_alloc_slow_v4(size, k); } ``` **削減対象**: - ❌ HAKMEM_TINY_BENCH_FASTPATH(SLL系) - ❌ TinyHotMag複数層 - ❌ wrapper context handling(slow pathへ) - ❌ 過剰なTLS変数(bcur/bendのみ保持) #### B-2: ACE簡素化(4ノブ×4状態) **期待効果**: p95安定化、ホットパス干渉除去 **実装内容**: - ノブ4つ: BATCH, HOT_THRESHOLD, drain_mask, slab_lg - 状態4つ: STEADY, BURST, REMOTE_HEAVY, MEM_TIGHT - tick=150ms、観測は1/16Kサンプル - ホットパス完全非干渉 ### Phase 4-C: 長期施策(2-4週間) #### C-1: Mid/Large TL-Segment **期待効果**: Mid/Large単スレで2×改善 **実装内容**: - 4-16KBページ単位のTLバンプ - ページ内free-list(連結生成最小化) - ≥64KBは直map再利用キャッシュ(LRU 64本) #### C-2: per-core arena + SPSC remote queue **期待効果**: MT競合削減、2-3×改善 **実装内容**: - スレッド→core固定 - cross-thread freeはSPSCリング - allocのついでにdrain(バッチ128-256) - レジストリcoreシャード化 ### マイルストーン更新 | Phase | Target | Expected Result | vs mimalloc | Status | |-------|--------|-----------------|-------------|---------| | Baseline | 16.24 M | 16.24 M | 67% | ✅ Baseline | | Phase 2+1 | 18.4-18.6 M | **16.53 M** | **68%** | ✅ 完了 (+1.8%) | | ~~Phase 3~~ | ~~17.5-18.0 M~~ | ~~16.57 M~~ | ~~68%~~ | ❌ 失敗 (+0.24%) | | **Phase 4-A** | **17.5-18.5 M** | - | **72-76%** | ⏳ **実装中** | | Phase 4-B | 19.0-20.0 M | - | 78-83% | 📋 設計完了 | | Phase 4-C | 22-24 M | - | 90-100% | 📐 構想中 | ### 48時間ロードマップ **Day 1 (今日)**: 1. ✅ ChatGPT Pro相談完了 2. ✅ ドキュメント更新 3. 🔧 Phase 4-A1実装(TLS-BUMP即値化) 4. 🔧 ビルド & ベンチマーク **Day 2 (明日)**: 1. 📊 Phase 4-A1結果分析 2. 🔧 Phase 4-A2実装(小マガジン128) 3. 📐 Phase 4-B詳細設計 4. 🚀 Phase 4-B実装開始判断 ### 設計原則 1. **2レジスタ経路死守**: `bcur/bend`だけでalloc完結 2. **層は最小3段**: `TLS-BUMP → 小マガジン → Slow` 3. **ホット/コールド完全分離**: データもコードも64B境界分離 4. **統計はサンプルのみ**: 1/16384、ホットパスに書き込みなし 5. **ヘッダ非更新**: slowで同期、allocはTLSのみ ### 受け入れ基準 - Tiny-Hot 32/64/128B: **mimalloc ≥90-110%** - Random Mixed: **mimalloc ≥90-105%**(p95安定) - Mid/Large単: **≥80-100%**(段階的) - Mid/Large MT: **×2改善** → 20-30%差まで短縮 - RSS: **予算±10%内**、MEM_TIGHTで守る --- --- ## 🚀 Phase 5: mimalloc 分析 & Direct Page Cache 実装 ### Phase 5 Overview **mimalloc 分析完了 (2025-11-03):** - 47% Gap の根本原因を特定 - 3つの詳細レポート作成 - Phase 3/4-A1 の教訓を確認: linked-list は最適 **Key Findings:** 1. **Direct Page Cache (O(1))** が最大のボトルネック: +15-20% 2. mimalloc も intrusive linked list を使用(Phase 3 の結論は正しい) 3. Gap はマイクロ最適化から来る(データ構造選択ではない) --- ### Phase 5-A: Direct Page Cache 実装 **Goal:** サイズ→ページ lookup を O(log n) から O(1) に **Current (HAKMEM):** ```c // Binary search through size classes - O(log n) int class_idx = hak_tiny_size_to_class(size); // 3-5 comparisons TinySlab* slab = g_tiny_pool.free_slabs[class_idx]; ``` **Target (mimalloc-style):** ```c // Direct array index - O(1) TinySlab* slab = g_tiny_pool.slabs_direct[size >> 3]; // 1 cycle! ``` **Implementation Plan:** **5-A-1. データ構造拡張** ```c // core/hakmem_tiny.h typedef struct { TinySlab* free_slabs[TINY_NUM_CLASSES]; // Existing TinySlab* full_slabs[TINY_NUM_CLASSES]; // Existing TinySlab* slabs_direct[129]; // NEW: Direct cache (8-1024B) // ... existing fields } TinyPool; ``` **5-A-2. Direct cache の更新** ```c // Slab allocation時に direct cache を populate static TinySlab* allocate_new_slab(int class_idx) { TinySlab* slab = /* ... existing allocation ... */; // NEW: Populate direct cache for this size class size_t block_size = g_tiny_class_sizes[class_idx]; for (size_t sz = block_size; sz < block_size + 8 && sz <= 1024; sz++) { int idx = sz >> 3; // size / 8 if (g_tiny_pool.slabs_direct[idx] == NULL) { g_tiny_pool.slabs_direct[idx] = slab; } } return slab; } // Slab exhaustion時に direct cache をクリア static void move_to_full_list(int class_idx, TinySlab* slab) { /* ... existing code ... */ // NEW: Clear direct cache entries pointing to this slab for (int i = 0; i < 129; i++) { if (g_tiny_pool.slabs_direct[i] == slab) { g_tiny_pool.slabs_direct[i] = NULL; } } } ``` **5-A-3. Hot path 書き換え** ```c void* hak_tiny_alloc(size_t size) { if (size > 1024 || size == 0) return NULL; // NEW: O(1) direct cache lookup int idx = size >> 3; // size / 8 TinySlab* slab = g_tiny_pool.slabs_direct[idx]; if (__builtin_expect(slab != NULL, 1)) { // Fast path: direct cache hit void* ptr = pop_from_slab(slab); if (__builtin_expect(ptr != NULL, 1)) { return ptr; // ← 5 cycles saved! } // Slab exhausted, clear cache and fallback g_tiny_pool.slabs_direct[idx] = NULL; } // Slow path: fallback to existing binary search int class_idx = hak_tiny_size_to_class(size); return hak_tiny_alloc_slow(class_idx); } ``` **Expected Results:** - **Cycle reduction:** 5 cycles per allocation (binary search elimination) - **Throughput:** +15-20% (16.53 → 19.0-19.8 M ops/sec) - **Memory overhead:** 1032 bytes (129 pointers) - **Risk:** Low (fallback to existing path on miss) **Timeline:** 1-2 days --- ### Phase 5-B: Dual Free Lists (Next) **Goal:** Local/Remote free list 分離で atomic ops 削減 **Expected Results:** +10-15% additional --- ### Phase 5-C: Branch Hints + Flags (Next) **Goal:** Predictable branch + bit-packed flags **Expected Results:** +5-8% additional --- ## 📊 Phase 5 Roadmap | Phase | Impact | Effort | Risk | Status | |-------|--------|--------|------|--------| | 5-A: Direct Cache | +15-20% | 1-2d | Low | 🔜 Next | | 5-B: Dual Free Lists | +10-15% | 3-5d | Med | ⏳ Pending | | 5-C: Branch Hints | +5-8% | 1-2d | Low | ⏳ Pending | | **Total** | **+45%** | **1-2w** | **Low** | **16.53 → 24.0 M ops/sec** | --- ## 🚀 Phase 6: Learning-Based Tiny Allocator (2025-11-02~) ### 戦略: "Simple Front + Smart Back" (Mid-Large HAKX の真似) **背景:** - Phase 5-B 失敗: Magazine unification で -71% 💀 - 包括的ベンチマークで根本原因特定: **Fast path が複雑すぎる** - Mid-Large HAKX は +171% で成功 → 同じアプローチを Tiny に適用 ### 目標 - **Phase 1 (1週間)**: Ultra-Simple Fast Path → System の 70-80% (95-108 M ops/sec) - **Phase 2 (1週間)**: 学習層追加 → System の 80-90% (108-122 M ops/sec) - **Phase 3 (1週間)**: メモリ効率最適化 → System 同等速度 + メモリで勝つ 🏆 ### 設計コンセプト #### Front: Ultra-Simple (System tcache 風) ```c void* hak_tiny_alloc(size_t size) { int cls = size_to_class_inline(size); void** head = &g_tls_cache[cls]; void* ptr = *head; if (ptr) { *head = *(void**)ptr; // 3-4 命令のみ! return ptr; } return hak_tiny_alloc_slow_adaptive(size, cls); } ``` #### Back: Smart (学習層) - **Class Hotness Tracking**: どのサイズが hot/cold か学習 - **動的キャッシュ容量調整**: Hot → 256 slots, Cold → 16 slots - **Adaptive Refill Count**: Miss rate に応じて 16-128 blocks ### Phase 1: Ultra-Simple Fast Path (進行中) **実装内容:** 1. `core/hakmem_tiny_simple.c` 新規作成 2. TLS Free List ベースの fast path (3-4 命令) 3. SuperSlab からの refill (既存を流用) **ファイル:** - `core/hakmem_tiny_simple.c` - シンプル版 Tiny allocator - `core/hakmem_tiny_simple.h` - ヘッダ **ベンチマーク:** - `bench_tiny_hot` で測定 - 目標: System の 70-80% ### 成功の鍵 1. **Fast path を System tcache と同等に** (3-4 命令) 2. **学習層で差別化** (動的容量調整) 3. **Mid-Large の成功パターンを適用** (+171% の実績) ### 関連ドキュメント - [`benchmarks/results/TINY_PERFORMANCE_ANALYSIS.md`](benchmarks/results/TINY_PERFORMANCE_ANALYSIS.md) - 根本原因分析 - [`benchmarks/results/BENCHMARK_SUMMARY_2025_11_02.md`](benchmarks/results/BENCHMARK_SUMMARY_2025_11_02.md) - 包括的ベンチマーク結果