diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 6878ee88..aef48d77 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -1,1613 +1,203 @@ -# CURRENT TASK (Phase 3d Series) – Hot/Cold Split + TLS Cache Merge +# CURRENT TASK – Tiny / SuperSlab / Shared Pool 最近まとめ -**Last Updated**: 2025-11-20 -**Owner**: Claude Code -**Size**: 約 1,360 行(全履歴保持版) +**Last Updated**: 2025-11-20 +**Scope**: Phase 3d / Tiny backend / Shared Pool / Small‑Mid / Mid‑Large crash fixes +**Note**: 古い詳細履歴は PHASE\* / REPORT\* 系のファイルに退避済み(このファイルは最近の要約だけを保持) --- -## 🎉 **Phase 3d-C: Hot/Cold Split - 完了** (2025-11-20) +## 1. Tiny Phase 3d – Hot/Cold Split 状況 -**成果**: Random Mixed 256B ベンチマーク **+10.8%** 改善 (22.6M → 25.0M ops/s) -**累積**: Phase 3c → Phase 3d-C で **+167%** 改善 (9.38M → 25.0M ops/s) +### 1.1 Phase 3d-C: Hot/Cold Split(完了) -### Phase 3d-C: Hot/Cold Split Architecture -- **設計**: SuperSlab内でホット(高使用率 >50%)/コールド(低使用率)slabを分離 -- **実装**: `core/box/ss_hot_cold_box.h` + `superslab_types.h` 拡張 -- **戦略**: Hot優先割り当てでキャッシュ局所性向上、index配列でメタデータアクセス削減 -- **結果**: +10.8%(期待範囲 +8-12%)、Phase 3d-B からの順調な改善 +- **目的**: SuperSlab 内で Hot slab(高利用率)を優先し、L1D ミス・分岐ミスを削減。 +- **主な変更**: + - `core/superslab/superslab_types.h` + - `hot_count / cold_count` + - `hot_indices[16] / cold_indices[16]` + - `core/box/ss_hot_cold_box.h` + - `ss_is_slab_hot()` – used > 50% を Hot 判定 + - `ss_update_hot_cold_indices()` – active slab を走査し index 配列を更新 + - `core/hakmem_tiny_superslab.c` + - `superslab_activate_slab()` で slab 活性化時に `ss_update_hot_cold_indices()` を呼ぶ +- **Perf(Random Mixed 256B, 100K ops)**: + - Phase 3d-B → 3d-C: **22.6M → 25.0M ops/s (+10.8%)** + - Phase 3c → 3d-C 累積: **9.38M → 25.0M ops/s (+167%)** -### Phase 3d-C 実装詳細 -**SuperSlab 拡張** (`superslab_types.h:81-85`): -```c -// Phase 3d-C: Hot/Cold Split - Cache locality optimization -uint8_t hot_count; // Number of hot slabs (high utilization) -uint8_t cold_count; // Number of cold slabs (low utilization) -uint8_t hot_indices[16]; // Indices of hot slabs (max 16) -uint8_t cold_indices[16]; // Indices of cold slabs (max 16) -``` +### 1.2 Phase 3d-D: Hot優先 refill(失敗 → revert 済み) -**Hot判定ロジック** (`ss_hot_cold_box.h:37-44`): -```c -static inline bool ss_is_slab_hot(const TinySlabMeta* meta) { - if (meta->capacity == 0) return false; - return (meta->used * 100 / meta->capacity) > 50; // >50% = hot -} -``` +- **試行内容(要約)**: + - `core/hakmem_shared_pool.c` の `shared_pool_acquire_slab()` Stage 2 を 2 パス構成に変更。 + - Pass 1: `ss->hot_indices[]` を優先スキャンして UNUSED slot を CAS 取得。 + - Pass 2: `ss->cold_indices[]` をフォールバックとしてスキャン。 + - 目的: Stage 2 内での「Hot slab 優先」を実現し、L1D/branch ミスをさらに削減。 -**Index更新** (`ss_hot_cold_box.h:48-80`): -- Scan active slabs、hot/cold分類してindex配列更新 -- L1D キャッシュフレンドリー(hot_indices[] = 16バイト = 1 cache line) +- **結果**: + - Random Mixed 256B ベンチ: **23.2M → 6.9M ops/s (-72%)** まで悪化。 -### Phase 3d-C Perf メトリクス (100K ops) +- **主な原因**: + 1. **`hot_count/cold_count` が実際には育っていない** + - 新規 SuperSlab 確保が支配的なため、Hot/Cold 情報が溜まる前に SS がローテーション。 + - その結果、`hot_count == cold_count == 0` で Pass1/2 がほぼ常時スキップされ、Stage 3 へのフォールバック頻度だけ増加。 + 2. **Stage 2 はそもそもボトルネックではない** + - SP-SLOT 導入後の統計では: + - Stage1 (EMPTY reuse): 約 5% + - Stage2 (UNUSED reuse): 約 92% + - Stage3 (new Superslab): 約 3% + - → Stage 2 内の「どの UNUSED slot を取るか」をいじっても、構造的には futex/mmap/L1 miss にはほぼ効かない。 + 3. **Shared Pool / Superslab の設計上、期待できる改善幅が小さい** + - Stage 2 のスキャンコストを O(スロット数) → O(hot+cold) に減らしても、全体の cycles のうち Stage 2 が占める割合が小さい。 + - 理論的な上限も高々数 % レベルにとどまる。 -| メトリック | 値 | 備考 | -|-----------|-----|------| -| Throughput | 25.0M ops/s | Phase 3d-B: 22.6M (+10.8%) | -| L1 D-cache misses | 409K (2.45%) | Phase 3c比で改善傾向 | -| Branch misses | 685K (8.08%) | Hot優先で分岐予測改善余地 | -| IPC | 1.06 | 改善中 | - -### Phase 3d Series 完全結果 - -| Phase | コミット | 性能 (1M ops) | 前回比 | 累積改善 | -|-------|---------|---------------|--------|----------| -| Phase 3c | 437df708e | 9.38M ops/s | - | - | -| **Phase 3d-A** | 38552c3f3 | (build error) | - | - | -| **Phase 3d-B** | 9b0d74640 | **22.6M ops/s** | - | **+141%** | -| **Phase 3d-C** | 23c0d9541 | **25.0M ops/s** | **+10.8%** | **+167%** | - -### 残りのギャップ分析(Perf統計付き) - -**Current Status**: -``` -HAKMEM: 25.0M ops/s (Phase 3d-C) -System: 78.4M ops/s (baseline) -Gap: 3.1倍遅い (32% of target) -``` - -**主要ボトルネック** (Phase 3d-C vs System, 100K ops): - -| メトリック | System | HAKMEM | 倍率 | 優先度 | -|-----------|--------|--------|------|--------| -| **L1 D-cache misses** | 44K (0.96%) | 449K (2.14%) | **10.1x** 💥 | P0 | -| **Cache references** | 81K | 1.5M | **18.7x** 💥💥 | P0 | -| **Branch misses** | 93K (4.68%) | 658K (7.51%) | **7.1x** 🔴 | P1 | -| **Cycles** | 5.6M | 41.2M | **7.3x** 🔴 | - | -| **Instructions** | 9.8M | 50.8M | **5.2x** 🔴 | - | - -**ボトルネック解析**: -1. **L1 D-cache miss (10.1倍)** 💥 - 最優先 - - 原因: SuperSlab metadata 分散アクセス - - Phase 3d-C で改善したが、まだ不十分 - - 対策: Phase 3d-D (Hot優先refill) + Metadata prefetch - -2. **Cache references (18.7倍)** 💥💥 - 致命的 - - 原因: L2/L3 cache への落下が多い - - SuperSlab 2MB 構造が大きすぎる - - 対策: SuperSlab サイズ縮小実験(2MB→1MB)、Hot metadata TLS cache - -3. **Branch misses (7.1倍)** 🔴 - - 原因: Refill path の分岐多数 - - Phase 3d-C の hot/cold分離で分岐予測改善余地 - - 対策: Phase 3d-D (Hot優先で予測性向上) - -### ❌ Phase 3d-D: Hot優先 refill - 失敗 (2025-11-20) - -**試行内容**: Shared Pool Stage 2 でHot/Cold indices優先スキャン - -**実装**: -- `hakmem_shared_pool.c` Stage 2を2パススキャンに変更 - - Pass 1: hot_indices[] から UNUSED slot を CAS claim - - Pass 2: cold_indices[] へフォールバック -- 直接 CAS で指定 slot を claim(`sp_slot_claim_lockfree()` をバイパス) - -**結果**: **性能悪化 -72%** (23.2M → 6.9M ops/s) ❌ - -**失敗原因**: -1. **hot_count/cold_count が 0 のまま** - - 新規 SuperSlab 確保時は hot/cold indices が未初期化 - - Pass 1/2 を全スキップ → Stage 3(新SS確保)へ直行 - - 無駄なループオーバーヘッドのみ追加 - -2. **ボトルネックに効かない設計ミス** - - Stage 2 は既に効率的(92%が Stage 2 hit、3%のみ Stage 3) - - Hot/Cold 順序変更は slot 選択の O(N) を O(hot+cold) に減らすだけ - - 本質的ボトルネック(futex/mmap/L1 cache miss)には無関係 - - 仮に動いても期待効果は数%程度(構造的限界) - -3. **測定条件の問題** - - Random Mixed ワークロードは新規 SS 確保が支配的 - - Hot/Cold 情報が育つ前に SS が切り替わる - - 再利用シナリオでのみ効果があるが、現ベンチマークでは発動しない - -**教訓** (ChatGPT先生フィードバック): -- **増分最適化の限界**: 局所的な改善では構造的ボトルネックは埋まらない -- **ボトルネック特定の重要性**: futex (Stage 1/2 ロック) / mmap (Stage 3) / L1 miss が真の敵 -- **Phase 12 Shared SuperSlab Pool の成果**: 既に Stage 2 hit 率 92% 達成済み -- **次の戦略**: Stage 2 内部の最適化ではなく、別のアプローチが必要 - -**Revert**: Phase 3d-C baseline に戻す(commit 23c0d9541) - -**検証** (Phase 3d-C 再確認): -- Run 1: 22.7M ops/s -- Run 2: 23.4M ops/s -- Run 3: 23.4M ops/s -- **Average: 23.2M ops/s** ✅ +- **結論**: + - Phase 3d-D の実装は **revert 済み**。 + - `shared_pool_acquire_slab()` Stage 2 は、Phase 3d-C 相当のシンプルな UNUSED スキャンに戻す。 + - Hot/Cold indices は今後の別の Box(例: Refill path 以外、学習層または可視化用途)で再利用候補。 --- -## 🎨 **Box Theory Refactoring - 完了** (2025-11-21) +## 2. hakmem_tiny.c Box Theory リファクタリング(進行中) -**成果**: hakmem_tiny.c を **2081行 → 562行 (-73%)** に削減、12モジュールを抽出 +### 2.1 目的 -### Phase 1: ChatGPT初期分割 (2081 → 1456行, -30%) -**実施内容**: -- `hakmem_tiny_config_box.inc` (211行) - サイズクラステーブル、デバッグカウンタ、ベンチマークマクロ -- `hakmem_tiny_publish_box.inc` (419行) - Publish/Adopt統計、TLSヘルパー関数、ライブキャップ管理 +- `core/hakmem_tiny.c` が 2000 行超で可読性・保守性が低下。 +- Box Theory に沿って役割ごとに箱を切り出し、**元の挙動を変えずに**翻訳単位内のレイアウトだけ整理する。 +- クロススレッド TLS / Superslab / Shared Pool など依存が重い部分は **別 .c に出さず `.inc` 化** のみに留める。 -**ビルド**: ✅ 成功(-10%性能低下は設計フェーズで許容) +### 2.2 これまでの分割(大きいところだけ) -**コミット**: 6b6ad69ac (Phase 1: Extract config_box + publish_box) +(※ 実際の詳細は各 `core/hakmem_tiny_*.inc` / `*_box.inc` を参照) -### Phase 2: Claude保守的抽出 (1456 → 616行, -58%) -**実施内容**: -1. **globals_box** (256行, lines 166-421) - グローバルプール、TLS変数、adopt_gate_try() -2. **legacy_slow_box** (96行, lines 190-285) - レガシーslab割り当てパス(未使用コールドパス) -3. **slab_lookup_box** (77行, lines 462-538) - O(1) registry lookup、hak_tiny_owner_slab() +- **Phase 1 – Config / Publish Box 抽出(今回の run を含む)** + - 新規ファイル: + - `core/hakmem_tiny_config_box.inc`(約 200 行) + - `g_tiny_class_sizes[]` + - `tiny_get_max_size()` + - Integrity カウンタ (`g_integrity_check_*`) + - Debug/bench マクロ (`HAKMEM_TINY_BENCH_*`, `HAK_RET_ALLOC`, `HAK_STAT_FREE` 等) + - `core/hakmem_tiny_publish_box.inc`(約 400 行) + - Publish/Adopt 統計 (`g_pub_*`, `g_slab_publish_dbg` など) + - Bench mailbox / partial ring (`bench_pub_*`, `slab_partial_*`) + - Live cap / Hot slot (`live_cap_for_class()`, `hot_slot_*`) + - TLS target helper (`tiny_tls_publish_targets()`, `tiny_tls_refresh_params()` 等) + - 本体側: + - `hakmem_tiny.c` から該当ブロックを削除し、同じ位置に `#include` を挿入。 + - 翻訳単位は維持されるため TLS / static 関数の依存はそのまま。 + - 効果: + - `hakmem_tiny.c`: **2081 行 → 1456 行**(約 -30%) + - ビルド: ✅ 通過(挙動は従来どおり)。 -**戦略**: 依存関係が少ないコアモジュールを優先抽出、各抽出後にビルド検証 +### 2.3 今後の候補(未実施、TODO) -**ビルド**: ✅ 全3モジュール成功 +- Frontend / fast-cache / TID キャッシュ周辺 + - `tiny_self_u32()`, `tiny_self_pt()` + - `g_fast_cache[]`, `g_front_fc_hit/miss[]` +- Phase 6 front gate wrapper + - `hak_tiny_alloc_fast_wrapper()`, `hak_tiny_free_fast_wrapper()` + - 周辺の debug / integrity チェック -**コミット**: 922eaac79 (Phase 2: Extract globals + legacy_slow + slab_lookup) - -### Phase 3: Task先生分析 + 保守的抽出 (616 → 562行, -9%) -**実施内容** (Task先生リスク分析に基づく): -1. **ss_active_box** (6行, lines 83-88) - SuperSlab active counter helpers (LOW risk ✅) - ```c - void ss_active_add(SuperSlab* ss, uint32_t n); - static inline void ss_active_inc(SuperSlab* ss); - ``` -2. **eventq_box** (32行, lines 241-272) - Event queue push、thread ID圧縮 (LOW risk ✅) -3. **sll_cap_box** (12行, lines 317-328) - SLL capacity policy (hot/cold classes) (LOW risk ✅) -4. **ultra_batch_box** (20行, lines 330-349) - Ultra batch size policy + override (LOW risk ✅) - -**戦略**: Task先生がLOWリスクと判定した4モジュールを順次抽出、MEDIUMリスクは今回スキップ - -**依存管理**: -- Forward declaration追加(registry_lookup等) -- #include順序最適化(globals_box → slab_lookup_box → 他) -- Static/TLS変数は.incで保持(分離.c化せず) - -**ビルド**: ✅ 全4モジュール成功、性能影響なし - -**コミット**: 287845913 (Phase 3: Extract ss_active + eventq + sll_cap + ultra_batch) - -### 最終成果サマリ - -| フェーズ | 削減量 | 累積行数 | 削減率 | 抽出モジュール | -|---------|-------|---------|-------|---------------| -| **Phase 1** | -625行 | 1456行 | -30% | 2モジュール (config, publish) | -| **Phase 2** | -840行 | 616行 | -58% | 3モジュール (globals, legacy_slow, slab_lookup) | -| **Phase 3** | -54行 | 562行 | -9% | 4モジュール (ss_active, eventq, sll_cap, ultra_batch) | -| **合計** | **-1519行** | **562行** | **-73%** | **12モジュール** | - -### Box Theory実装パターン - -**抽出パターン**: -```c -// Before (hakmem_tiny.c) -static inline void ss_active_add(SuperSlab* ss, uint32_t n) { - atomic_fetch_add_explicit(&ss->total_active_blocks, n, memory_order_relaxed); -} - -// After (hakmem_tiny.c) -#include "hakmem_tiny_ss_active_box.inc" - -// New file (core/hakmem_tiny_ss_active_box.inc) -void ss_active_add(SuperSlab* ss, uint32_t n) { - atomic_fetch_add_explicit(&ss->total_active_blocks, n, memory_order_relaxed); -} -``` - -**利点**: -- ✅ 同一翻訳単位内(.inc)でstatic/TLS変数が正常動作 -- ✅ Forward declarationで循環依存解決 -- ✅ モジュール境界明確化(将来的に.c化も可能) -- ✅ 段階的リファクタリングでビルド安定性維持 - -### 失敗した試み(学び) - -**Phase 2 失敗例: lifecycle.inc → lifecycle.c 分離** -- **問題**: g_tls_lists、g_empty_lock等の複雑な依存、helper関数のコピー必要 -- **対応**: Revert、.incパターンが正解と判断 - -**Phase 3 失敗例: 6モジュール一括抽出(アグレッシブ)** -- **問題**: helpers_boxで未定義シンボル(g_use_superslab等)、依存順序エラー -- **対応**: Revert、Task先生分析 → LOWリスク4モジュールのみ抽出 - -**教訓**: -1. **依存分析が最優先** - Task先生のリスク評価に従う -2. **小刻みな抽出** - 1-4モジュールずつ、毎回ビルド検証 -3. **.incパターンの有効性** - 無理に.c化せず、境界明確化を優先 - -### 残り抽出候補(Task先生分析) - -**MEDIUMリスク** (今回スキップ、次回検討): -- **Candidate 5**: Hot/Cold判定helpers (12行) - is_hot_class()等 -- **Candidate 6**: Frontend helpers (18行) - tiny_optional_push()等 - -**推奨**: 性能最適化フェーズ終了後に実施(現在は設計詰め段階優先) - -### 影響 - -**可読性**: ✅ 大幅改善(2081 → 562行、モジュール境界明確) -**保守性**: ✅ 改善(変更箇所の特定が容易) -**ビルド時間**: 影響なし(.incで同一翻訳単位) -**性能**: -10% (Phase 1のみ、Phase 2/3は影響なし) - 設計フェーズで許容 -**安定性**: ✅ 全ビルド成功、クラッシュなし - -### 次のステップ - -**優先度1**: Phase 3d-D代替案(Hot優先refill失敗の対策) -**優先度2**: Phase 12 Shared SuperSlab Pool(根本的な性能改善) -**優先度3**: 残りMEDIUMリスクモジュール抽出(設計最適化完了後) +**方針**: +- どれも「別 .c ではなく `.inc` として hakmem_tiny.c から `#include` される」形に統一し、 + TLS や static 変数のスコープを壊さない。 --- -## 📊 Phase 3d-B: TLS Cache Merge - 完了 (2025-11-20) +## 3. SuperSlab / Shared Pool の現状要約 -**成果**: +141% 改善 (9.38M → 22.6M ops/s) +### 3.1 SuperSlab 安定化(Phase 6-2.x) -### Phase 3d-B: TLS Cache Merge -- **設計**: g_tls_sll_head[] + g_tls_sll_count[] → 統合 g_tls_sll[] struct -- **目的**: L1D キャッシュミス削減(2 loads → 1 load) -- **実装**: 20+ ファイル更新(Box API統一、TLS配列統合) +- **主な問題**: + - Guess loop が unmapped 領域を `magic` 読みして SEGV。 + - Tiny free が Superslab を見つけられなかったケースで fallback しきれず崩壊。 +- **主な修正**: + - Guess loop 削除(`hakmem_free_api.inc.h`)。 + - Superslab registry の `_Atomic uintptr_t base` 化、acquire/release の統一。 + - Fallback 経路のみ `hak_is_memory_readable()`(mincore ベース)で safety check を実施。 +- **結果**: + - Random Mixed / mid_large_mt などでの SEGV は解消。 + - mincore は fallback 経路限定のため、ホットパスへの影響は無視できるレベル。 -### 旧Phase 26: Front Gate Unification (ChatGPT先生提案) -- **設計**: malloc → hak_alloc_at (236行) → wrapper → tiny_alloc_fast の **3層オーバーヘッド削減** -- **実装**: `core/front/malloc_tiny_fast.h` + `core/box/hak_wrappers.inc.h` 統合 -- **戦略**: Tiny範囲(≤1024B)専用の単層直行経路、Phase 23 Unified Cache 活用 -- **ENV**: `HAKMEM_FRONT_GATE_UNIFIED=1` でデフォルトOFF → **本番投入推奨** +### 3.2 SharedSuperSlabPool(Phase 12 SP-SLOT Box) -### Phase 26 実装詳細 -**malloc_tiny_fast()** (alloc fast path): -```c -1. size → class_idx (inline table lookup, 1-2 instructions) -2. unified_cache_pop_or_refill(class_idx) (Phase 23 tcache, 2-3 cache misses) -3. Write header + return USER pointer (2-3 instructions) -Total: 8-10 instructions (vs 3-layer cascade: 236 lines routing + diagnostics) -``` - -**free_tiny_fast()** (free fast path): -```c -1. Page boundary guard (offset_in_page == 0 → return 0) -2. Read header + validate Tiny magic (0xa0-0xa7) -3. unified_cache_push(class_idx, base) (Phase 23 tcache, 2-3 cache misses) -Total: 6-8 instructions (vs classify_ptr + hak_free_at routing) -``` - -### Phase 26 修正したバグ -1. **初期化バグ**: Phase 26 fast path が hak_init() をバイパス → `if (!g_initialized) hak_init()` 追加 -2. **ページ境界SEGV**: free_tiny_fast() がページ先頭 (offset==0) で前ページ読み → ガード追加 - ```c - uintptr_t off = (uintptr_t)ptr & 0xFFFu; - if (off == 0) return 0; // Page-aligned → 通常 free 経路へ - ``` - -### A/B ベンチマーク結果 (Random Mixed 256B, 100K iterations) -| Configuration | Run 1 | Run 2 | Run 3 | **Average** | vs Baseline | -|---------------|-------|-------|-------|-------------|-------------| -| **Phase 26 OFF** | 11.21M | 11.02M | 11.76M | **11.33M ops/s** | Baseline | -| **Phase 26 ON** | 13.21M | 12.55M | 12.62M | **12.79M ops/s** | **+12.9%** 🎯 | - -**ChatGPT先生の予測**: +10-15% (3層オーバーヘッド削減による改善) -**実測結果**: **+12.9%** ← **予測ど真ん中!** 🎯 - -### 本番推奨設定 (Phase 23 + Phase 26 組み合わせ) -```bash -export HAKMEM_TINY_UNIFIED_CACHE=1 # Phase 23: Hot_2048がデフォルト -export HAKMEM_FRONT_GATE_UNIFIED=1 # Phase 26: Front Gate Unification -./out/release/bench_random_mixed_hakmem -# Expected: 12.79M ops/s (+27.8% vs Phase 23前のbaseline 10.0M ops/s) -``` - -**主要ファイル**: -- `core/front/malloc_tiny_fast.h` - Phase 26 single-layer malloc/free implementation -- `core/box/hak_wrappers.inc.h:128-143` - Phase 26 fast path integration (malloc) -- `core/box/hak_wrappers.inc.h:179-190` - Phase 26 fast path integration (free) +- **構造**: + - Stage1: EMPTY slot reuse(per-class free list / lock-free freelist) + - Stage2: UNUSED slot reuse(`SharedSSMeta` + lock-free CAS) + - Stage3: 新規 Superslab(LRU pop → mmap) +- **成果**: + - SuperSlab 新規確保(mmap/munmap)呼び出しをおよそ **-48%** 削減。 + - 「毎回 mmap」状態からは脱出。 +- **残る課題**: + - Larson / 一部 workload で `shared_pool_acquire_slab()` が CPU 時間の大半を占める。 + - Stage3 の mutex 頻度・待ち時間が高く、futex が syscall time の ~70% というケースもある。 + - Warm Superslab を長く保持する SS-Reuse ポリシーがまだ弱い。 --- -## 🎉 **Phase 23/25: Unified Frontend Cache - 完了** (2025-11-17) +## 4. Small‑Mid / Mid‑Large – Crash 修正と現状 -**成果**: Random Mixed 256B ベンチマーク **+7.3%** 改善 (10.58M → 11.35M ops/s) +### 4.1 Mid‑Large Crash FIX(2025-11-16) -### Phase 23: Unified Cache Implementation -- **設計**: tcache-style single-layer frontend (Ring → FastCache → SFC → SLL の 4 層を 1 層に統合) -- **実装**: `core/front/tiny_unified_cache.{h,c}` - Array-based ring buffer (2-3 cache misses) -- **統合**: Alloc path (`tiny_alloc_fast.inc.h:621-633`) + Free path (`hak_free_api.inc.h`) -- **ENV**: `HAKMEM_TINY_UNIFIED_CACHE=1` でデフォルトOFF → **Hot_2048設定で本番投入** +- **症状**: + - Mid‑Large / VM Mixed ベンチで `free(): invalid pointer` → 即 SEGV。 +- **Root Cause**: + - `classify_ptr()` が Mid‑Large の `AllocHeader` を見ておらず、`PTR_KIND_UNKNOWN` と誤分類。 + - Free wrapper が `PTR_KIND_MID_LARGE` ケースを処理していなかった。 +- **修正**: + - `classify_ptr()` に AllocHeader チェックを追加し、`PTR_KIND_MID_LARGE` を返す。 + - Free wrapper に `PTR_KIND_MID_LARGE` ケースを追加して HAKMEM 管轄として処理。 +- **結果**: + - Mid‑Large MT (8–32KB): 0 → **10.5M ops/s**(System 8.7M の **120%**)。 + - VM Mixed: 0 → **285K ops/s**(System 939K の 30.4%)。 + - Mid‑Large 系のクラッシュは解消。 -### Phase 23 Capacity Optimization (Hot_2048) -- **Task Agent**: 10 configurations × 3 runs = 35 benchmarks -- **最適設定**: C2/C3 (128B/256B) = 2048 slots, 他 = 64 slots -- **根拠**: Hot-class優先戦略が+6.2%の追加改善(vs All_128) -- **メモリ**: ~1.1MB cache overhead (C2/C3 に集中配置) +### 4.2 random_mixed / Larson Crash FIX(2025-11-16) -### Phase 25-A: Header Read Optimization (+2.2%) -- **削減**: FG_DOMAIN_TINY の重複 header read を除去 -- **L1 hit**: 2回目の header read は L1 cache hit (~1 cycle) → 効果限定的 +- **random_mixed**: + - Mid‑Large fix に追加した AllocHeader 読みがページ境界を跨いで SEGV を起こしていた。 + - 修正: ページ内オフセットがヘッダサイズ以上のときだけヘッダを読むようにガード。 + - 結果: SEGV → **1.9M ops/s** 程度まで回復。 -### Phase 25-B-1: Promote-on-Full (REVERTED, -4.0%) -- **失敗**: Smart promotion logic が overhead > benefit -- **教訓**: Clever ≠ Fast、incremental最適化は限界に達した - -### Debug Log修正 (性能改善) -- **修正箇所**: `core/tiny_refill_opt.h:316-326`, `core/box/ss_hot_prewarm_box.c:143-146` -- **問題**: `[C2_CARVE]` / `[BOX_SS_HOT_PREWARM]` が Release build で常時出力 -- **解決**: `#if !HAKMEM_BUILD_RELEASE` で囲み、stderr負荷を除去 - -### 本番推奨設定 -```bash -export HAKMEM_TINY_UNIFIED_CACHE=1 # Hot_2048がデフォルト(C2/C3=2048, 他=64) -./out/release/bench_random_mixed_hakmem -``` - -**次の戦略**: Phase 23でfrontend最適化は限界、Phase 12 Shared SuperSlab Pool (backend根本解決) へ進む +- **Larson**: + - Layer1: cross-thread free が TLS SLL を破壊していた → `owner_tid_low` による cross‑thread 判定を常時 ON にし、remote queue に退避。 + - Layer2: `MAX_SS_METADATA_ENTRIES` が 2048 で頭打ち → 8192 に拡張。 + - 結果: クラッシュ・ハングは解消(性能はまだ System に遠く及ばない)。 --- -## 1. 全体の現在地(どこまで終わっているか) +## 5. TODO(短期フォーカス) -- Tiny (0–1023B) - - NEW 3-layer front(bump / small_mag / slow)安定。 - - TinyHeapV2: 「alloc フロント+統計」は実装済みだが、実運用は **C2/C3 を UltraHot に委譲**。 - - TinyUltraHot(Phase 14): - - C2/C3(16B/32B)専用 L0 ultra-fast path(Stealing モデル)。 - - 固定サイズベンチで +16〜36% 改善、hit 率 ≈ 100%。 - - Box 分離(Phase 15): - - free ラッパが外部ポインタまで `hak_free_at` に投げていた問題を修正。 - - BenchMeta(slots など)→ 直接 `__libc_free`、CoreAlloc(Tiny/Mid)→ `hak_free_at` の二段構えに整理。 +**Tiny / Backend** +- [ ] SS-Reuse Box の設計 + - Superslab 単位の再利用戦略を整理(EMPTY slab の扱い、Warm SS の寿命、LRU と shared_pool の関係)。 +- [ ] `shared_pool_acquire_slab()` Stage3 の観測強化 + - futex 回数 / 待ち時間のカウンタを追加し、「どのクラスが new Superslab を乱発しているか」を可視化。 -- Mid / PoolTLS(1KB–32KB) - - PoolTLS Phase 完了(Mid-Large MT ベンチ) - - ~10.6M ops/s(system malloc より速い構成あり)。 - - lock contention(futex 68%)を lock-free MPSC + bind box で大幅削減。 - - GAP 修正(Tiny 1023B / Mid 1KB〜): - - `TINY_MAX_SIZE=1023` / `MID_MIN_SIZE=1024` で 1KB–8KB の「誰も扱わない帯」は解消済み。 +**Tiny / Frontend(軽め)** +- [ ] C2/C3 Hot Ring Cache proto(Phase 21-1) + - Ring → SLL → Superslab の階層を C2/C3 のみ先行実装して、効果と複雑さを評価。 -- Shared SuperSlab Pool(Phase 12 – SP-SLOT Box) - - 1 SuperSlab : 多 class 共有 + SLOT_UNUSED/ACTIVE/EMPTY 追跡。 - - SuperSlab 数: 877 → 72(-92%)、mmap/munmap: -48%、Throughput: +131%。 - - Lock contention P0-5 まで実装済み(Stage 2 lock-free claiming)。 - -- ExternalGuard(Phase 15) - - UNKNOWN ポインタ(Tiny/Pool/Mid/L25/registry どこでも捕まらないもの)を最後の箱で扱う。 - - 挙動: - - `hak_super_lookup` など全て miss → mincore でページ確認 → 原則「解放せず leak 扱い(安全優先)」。 - - Phase 15 修正で: - - BenchMeta のポインタを CoreAlloc に渡さなくなり、UNKNOWN 呼び出し回数が激減。 - - `mincore` の CPU 負荷もベンチではほぼ無視できるレベルまで縮小。 +**Small‑Mid / Mid‑Large** +- [ ] Small‑Mid Box(Phase 17)のコードは保持しつつ、デフォルト OFF を維持(実験結果のアーカイブとして残す)。 +- [ ] Mid‑Large / VM Mixed の perf 改善は、Tiny/Backend 安定化後に再検討。 --- -## 2. Tiny 性能の現状(Phase 14–15 時点) +## 6. 古い詳細ログへのリンク -### 2.1 Fixed-size Tiny ベンチ(HAKMEM vs System) +この CURRENT_TASK は「直近フェーズのダイジェスト専用」です。 +歴史的な詳細ログや試行錯誤の全文は、以下のファイル群を参照してください: -**Phase 21-1: Ring Cache Implementation (C2/C3/C5) (2025-11-16)** 🎯 -- **Goal**: Eliminate pointer chasing in TLS SLL by using array-based ring buffer cache -- **Strategy**: 3-layer hierarchy (Ring L0 → SLL L1 → SuperSlab L2) -- **Implementation**: - - Added `TinyRingCache` struct with power-of-2 ring buffer (128 slots default) - - Implemented `ring_cache_pop/push` for ultra-fast alloc/free (1-2 instructions) - - Extended to C2 (32B), C3 (64B), C5 (256B) size classes - - ENV variables: `HAKMEM_TINY_HOT_RING_ENABLE=1`, `HAKMEM_TINY_HOT_RING_C2/C3/C5=128` -- **Results** (`bench_random_mixed_hakmem 500K, 256B workload`): - - **Baseline** (Ring OFF): 20.18M ops/s - - **C2/C3 Ring**: 21.15M ops/s (**+4.8%** improvement) ✅ - - **C2/C3/C5 Ring**: 21.18M ops/s (**+5.0%** total improvement) ✅ -- **Analysis**: - - C2/C3 provide most of the gain (small sizes are hottest) - - C5 addition provides marginal benefit (+0.03M ops/s) - - Implementation complete and stable -- **Files Modified**: - - `core/front/tiny_ring_cache.h/c` - Ring buffer implementation - - `core/tiny_alloc_fast.inc.h` - Alloc path integration - - `core/tiny_free_fast_v2.inc.h` - Free path integration (line 154-160) +- Tiny / Frontend / Phase 23–26: + - `PHASE23_CAPACITY_OPTIMIZATION_RESULTS.md` + - `PHASE25_*`, `PHASE26_*` 系ドキュメント +- SuperSlab / Shared Pool / Backend: + - `PHASE12_SHARED_SUPERSLAB_POOL_DESIGN.md` + - `PHASE12_SP_SLOT_BOX_IMPLEMENTATION_REPORT.md` + - `BOTTLENECK_ANALYSIS_REPORT_20251114.md` +- Small‑Mid / Mid‑Large / Larson: + - `MID_LARGE_*` 系レポート + - `LARSON_*` 系レポート + - `P0_*`, `CRITICAL_BUG_REPORT.md` ---- +必要になったら、これらから個別に掘り起こして Box 単位で議論・実装していく方針です。 -**Phase 21-1-D: Ring Cache Default ON (2025-11-16)** 🚀 -- **Goal**: Enable Ring Cache by default for production use (remove ENV gating) -- **Implementation**: 1-line change in `core/front/tiny_ring_cache.h:72` - - Changed logic: `g_enable = (e && *e == '0') ? 0 : 1; // DEFAULT: ON` - - ENV=0 disables, ENV unset or ENV=1 enables -- **Results** (`bench_random_mixed_hakmem 500K, 256B workload, 3-run average`): - - **Ring ON** (default): **20.31M ops/s** (baseline) - - **Ring OFF** (ENV=0): 19.30M ops/s - - **Improvement**: **+5.2%** (+1.01M ops/s) ✅ -- **Impact**: Ring Cache now active in all builds without manual ENV configuration - ---- - -**Performance Bottleneck Analysis (Task-sensei Report, 2025-11-16)** 🔍 - -**Root Cause: Cache Misses (6.6x worse than System malloc)** -- **L1 D-cache miss rate**: HAKMEM 5.15% vs System 0.78% → **6.6x higher** -- **IPC (instructions/cycle)**: HAKMEM 0.52 vs System 1.43 → **2.75x worse** -- **Branch miss rate**: HAKMEM 11.86% vs System 4.77% → **2.5x higher** -- **Per-operation cost**: HAKMEM **8-10 cache misses** vs System **2-3 cache misses** - -**Problem: 4-5 Layer Frontend Cascade** -``` -Random Mixed allocation flow: - Ring (L0) miss → FastCache (L1) miss → SFC (L2) miss → TLS SLL (L3) miss → SuperSlab refill (L4) - = 8-10 cache misses per allocation (each layer = 2 misses: head + next pointer) -``` - -**System malloc tcache: 2-3 cache misses (single-layer array-based bins)** - -**Improvement Roadmap** (Target: 48-77M ops/s, System比 53-86%): -1. **P1 (Done)**: Ring Cache default ON → **+5.2%** (20.3M ops/s) ✅ -2. **P2 (Next)**: Unified Frontend Cache (flatten 4-5 layers → 1 layer) → **+50-100%** (30-40M expected) -3. **P3**: Adaptive refill optimization → **+20-30%** -4. **P4**: Branchless dispatch table → **+10-15%** -5. **P5**: Metadata locality optimization → **+15-20%** - -**Conservative Target**: 48M ops/s (+136% vs current, 53% of System) -**Optimistic Target**: 77M ops/s (+279% vs current, 86% of System) - ---- - -**Phase 22: Lazy Per-Class Initialization (2025-11-16)** 🚀 -- **Goal**: Cold-start page faultを削減 (ChatGPT分析: `hak_tiny_init()` → 94.94% of page faults) -- **Strategy**: Eager init (全8クラス初期化) → Lazy init (使用クラスのみ初期化) -- **Results** (`bench_random_mixed_hakmem 500K, 256B workload`): - - **Cold-start**: 18.1M ops/s (Phase 21-1: 16.2M) → **+12% improvement** ✅ - - **Steady-state**: 25.5M ops/s (Phase 21-1: 26.1M) → -2.3% (誤差範囲) -- **Key Achievement**: `hak_tiny_init.part.0` 完全削除、未使用クラスのpage touchを回避 -- **Remaining Bottleneck**: SuperSlab allocation時の`memset` page fault (42.40%) - ---- - -**📊 PERFORMANCE MAP (2025-11-16) - 全体性能俯瞰** 🗺️ - -ベンチマーク自動化スクリプト: `scripts/bench_performance_map.sh` -最新結果: `bench_results/performance_map/20251116_095827/` - -### 🎯 固定サイズ (16-1024B) - Tiny層の現実 - -| Size | System | HAKMEM | Ratio | Status | -|------|--------|--------|-------|--------| -| 16B | 118.6M | 50.0M | 42.2% | ❌ Slow | -| 32B | 103.3M | 49.3M | 47.7% | ❌ Slow | -| 64B | 104.3M | 49.2M | 47.1% | ❌ Slow | -| **128B** | **74.0M** | **51.8M** | **70.0%** | **⚠️ Gap** ✨ | -| 256B | 115.7M | 36.2M | 31.3% | ❌ Slow | -| 512B | 103.5M | 41.5M | 40.1% | ❌ Slow | -| 1024B| 96.0M | 47.8M | 49.8% | ❌ Slow | - -**発見**: -- **128Bのみ 70%** (唯一Gap範囲) - 他は全て50%未満 -- **256Bが最悪 31.3%** - Phase 22で18.1M → 36.2Mに改善したが、systemの1/3に留まる -- **小サイズ (16-64B) 42-47%** - UltraHot経由でも system の半分 - -### 🌀 Random Mixed (128B-1KB) - -| Allocator | ops/s | vs System | -|-----------|--------|-----------| -| System | 90.2M | 100% (baseline) | -| **Mimalloc** | **117.5M** | **130%** 🏆 (systemより速い!) | -| **HAKMEM** | **21.1M** | **23.4%** ❌ (mimallocの1/5.5) | - -**衝撃的発見**: -- Mimallocは system より 30%速い -- HAKMEMは mimalloc の **1/5.5** - 巨大なギャップ - -### 💥 CRITICAL ISSUES - Mid-Large / MT層が完全破壊 - -**Mid-Large MT (8-32KB)**: ❌ **CRASHED** (コアダンプ) -- **原因**: `hkm_ace_alloc` が 33KB allocation で NULL返却 -- **結果**: `free(): invalid pointer` → クラッシュ -- **Mimalloc**: 40.2M ops/s (system の 449%!) -- **HAKMEM**: 0 ops/s (動作不能) - -**VM Mixed**: ❌ **CRASHED** (コアダンプ) -- System: 957K ops/s -- HAKMEM: 0 ops/s - -**Larson (MT churn)**: ❌ **SEGV** -- System: 3.4M ops/s -- Mimalloc: 3.4M ops/s -- HAKMEM: 0 ops/s - ---- - -**🔧 Mid-Large Crash FIX (2025-11-16)** ✅ - -**Root Cause (ChatGPT分析)**: -- `classify_ptr()` が AllocHeader (Mid/Large mmap allocations) をチェックしていない -- Free wrapper が `PTR_KIND_MID_LARGE` ケースを処理していない -- 結果: Mid-Large ポインタが `PTR_KIND_UNKNOWN` → `__libc_free()` → `free(): invalid pointer` - -**修正内容**: -1. **`classify_ptr()` に AllocHeader チェック追加** (`core/box/front_gate_classifier.c:256-271`) - - `hak_header_from_user()` + `hak_header_validate()` で HAKMEM_MAGIC 確認 - - `ALLOC_METHOD_MMAP/POOL/L25_POOL` → `PTR_KIND_MID_LARGE` 返却 -2. **Free wrapper に `PTR_KIND_MID_LARGE` ケース追加** (`core/box/hak_wrappers.inc.h:181`) - - `is_hakmem_owned = 1` で HAKMEM 管轄として処理 - -**修正結果**: -- **Mid-Large MT (8-32KB)**: 0 → **10.5M ops/s** (System 8.7M = **120%**) 🏆 -- **VM Mixed**: 0 → **285K ops/s** (System 939K = 30.4%) -- ✅ クラッシュ完全解消、Mid-Large で system malloc を **20% 上回る** - -**残存課題**: -- ❌ **random_mixed**: SEGV (AllocHeader読み込みでページ境界越え) -- ❌ **Larson**: SEGV継続 (Tiny 8-128B 領域、別原因) - ---- - -**🔧 random_mixed Crash FIX (2025-11-16)** ✅ - -**Root Cause**: -- Mid-Large fix で追加した `classify_ptr()` の AllocHeader check が unsafe -- AllocHeader = 40 bytes → `ptr - 40` がページ境界越えると SEGV -- 例: `ptr = 0x7ffff6a00000` (page-aligned) → header at `0x7ffff69fffd8` (別ページ、unmapped) - -**修正内容** (`core/box/front_gate_classifier.c:263-266`): -```c -// Safety check: Need at least HEADER_SIZE (40 bytes) before ptr -uintptr_t offset_in_page_for_hdr = (uintptr_t)ptr & 0xFFF; -if (offset_in_page_for_hdr >= HEADER_SIZE) { - // Safe to read AllocHeader (won't cross page boundary) - AllocHeader* hdr = hak_header_from_user(ptr); - ... -} -``` - -**修正結果**: -- **random_mixed**: SEGV → **1.92M ops/s** ✅ -- ✅ Single-thread workloads 完全修復 - ---- - -**🔧 Larson MT Crash FIX (2025-11-16)** ✅ - -**2-Layer Problem Structure**: - -**Layer 1: Cross-thread Free (TLS SLL Corruption)** -- **Root Cause**: Block allocated by Thread A, freed by Thread B → pushed to B's TLS SLL - - B allocates the block → metadata still points to A's SuperSlab → corruption - - Poison values (0xbada55bada55bada) in TLS SLL → SEGV in `tiny_alloc_fast()` -- **Fix** (`core/tiny_free_fast_v2.inc.h:176-205`): - - Made cross-thread check **ALWAYS ON** (removed ENV gating) - - Check `owner_tid_low` on every free, route cross-thread to remote queue via `tiny_free_remote_box()` -- **Status**: ✅ **FIXED** - TLS SLL corruption eliminated - -**Layer 2: SP Metadata Capacity Limit** -- **Root Cause**: `[SP_META_CAPACITY_ERROR] Exceeded MAX_SS_METADATA_ENTRIES=2048` - - Larson rapid churn workload → 2048+ SuperSlabs → registry exhaustion → hang -- **Fix** (`core/hakmem_shared_pool.h:122-126`): - - Increased `MAX_SS_METADATA_ENTRIES` from 2048 → **8192** (4x capacity) -- **Status**: ✅ **FIXED** - Larson completes successfully - -**Results** (10 seconds, 4 threads): -- **Before**: 4.2TB virtual memory, 65,531 mappings, indefinite hang (kill -9 required) -- **After**: 6.7GB virtual (-99.84%), 424MB RSS, completes in 10-18 seconds -- **Throughput**: 7,387-8,499 ops/s (0.014% of system malloc 60.6M) - -**Layer 3: Performance Optimization (IN PROGRESS)** -- Cross-thread check adds SuperSlab lookup on every free (20-50 cycles overhead) -- **Drain Interval Tuning** (2025-11-16): - - Baseline (drain=2048): 7,663 ops/s - - Moderate (drain=1024): **8,514 ops/s** (+11.1%) ✅ - - Aggressive (drain=512): Core dump ❌ (too aggressive, causes crash) -- **Recommendation**: `export HAKMEM_TINY_SLL_DRAIN_INTERVAL=1024` for stable +11% gain -- **Remaining Work**: LRU policy tuning (MAX_CACHED, MAX_MEMORY_MB, TTL_SEC) -- Goal: Improve from 0.014% → 80% of system malloc (currently 0.015% with drain=1024) - ---- - -### 📈 Summary (Performance Map 2025-11-16 17:15) - -**修正後の全体結果**: -- ✅ Competitive (≥80%): **0/10 benchmarks** (0%) -- ⚠️ Gap (50-80%): **1/10 benchmarks** (10%) ← 64B固定のみ 53.6% -- ❌ Slow (<50%): **9/10 benchmarks** (90%) - -**主要ベンチマーク**: -1. **Fixed-size (16-1024B)**: 38.5-53.6% of system (64B が最良) -2. **Random Mixed (128-1KB)**: **19.4M ops/s** (24.0% of system) -3. **Mid-Large MT (8-32KB)**: **891K ops/s** (12.1% of system, crash 修正済み ✅) -4. **VM Mixed**: **275K ops/s** (30.7% of system, crash 修正済み ✅) -5. **Larson (MT churn)**: **7.4-8.5K ops/s** (0.014% of system, crash 修正済み ✅, 性能最適化は Layer 3 で対応予定) - -**優先課題 (2025-11-16 更新)**: -1. ✅ **完了**: Mid-Large crash 修復 (classify_ptr + AllocHeader check) -2. ✅ **完了**: VM Mixed crash 修復 (Mid-Large fix で解消) -3. ✅ **完了**: random_mixed crash 修復 (page boundary check) -4. 🔴 **P0**: Larson SP metadata limit 拡大 (2048 → 4096-8192) -5. 🟡 **P1**: Fixed-size 性能改善 (38-53% → 目標 80%+) -6. 🟡 **P1**: Random Mixed 性能改善 (24% → 目標 80%+) -7. 🟡 **P1**: Mid-Large MT 性能改善 (12% → 目標 80%+, mimalloc 449%が参考値) - -`bench_fixed_size_hakmem` / `bench_fixed_size_system`(workset=128, 500K iterations 相当) - -| Size | HAKMEM (Phase 15) | System malloc | 比率 | -|--------|-------------------|---------------|----------| -| 128B | ~16.6M ops/s | ~90M ops/s | ~18.5% | -| 256B | ~16.2M ops/s | ~89.6M ops/s | ~18.1% | -| 512B | ~15.0M ops/s | ~90M ops/s | ~16.6% | -| 1024B | ~15.1M ops/s | ~90M ops/s | ~16.8% | - -状態: -- クラッシュは完全解消(workset=64/128 で長尺 500K iter も安定)。 -- Tiny UltraHot + 学習層 + ExternalGuard の組み合わせは「正しさ」は OK。 -- 性能は system の ~16–18% レベル(約 5–6× 遅い)→ まだ大きな伸びしろあり。 - -### 2.2 C2/C3 UltraHot 専用ベンチ - -固定サイズ(100K iterations, workset=128) - -| Size | Baseline (UltraHot OFF) | UltraHot ON | 改善率 | Hit Rate | -|------|-------------------------|-------------|-------------|---------| -| 16B | ~40.4M ops/s | ~55.0M | +36.2% 🚀 | ≈100% | -| 32B | ~43.5M ops/s | ~50.6M | +16.3% 🚀 | ≈100% | - -Random Mixed 256B: -- Baseline: ~8.96M ops/s -- UltraHot ON: ~8.81M ops/s(-1.6%、誤差〜軽微退化) -- 理由: C2/C3 が全体の 1–2% のみ → UltraHot のメリットが平均に薄まる。 - -結論: -- C2/C3 UltraHot は **ターゲットクラスに対しては実用級の Box**。 -- 他ワークロードでは「ほぼ影響なし(わずかな分岐オーバーヘッドのみ)」の範囲に収まっている。 - ---- - -## 3. Phase 15: ExternalGuard / Domain 分離の成果 - -### 3.1 以前の問題 - -- free ラッパ(`core/box/hak_wrappers.inc.h`)が: - - HAKMEM 所有かチェックせず、すべての `free(ptr)` を `hak_free_at(ptr, …)` に投げていた。 - - その結果: - - ベンチ内部 `slots`(`calloc(256, sizeof(void*))` の 2KB など)も CoreAlloc に流入。 - - `classify_ptr` → UNKNOWN → ExternalGuard → mincore → 「解放せず leak」と判定。 - - ベンチ観測: - - 約 0.84% の leak(BenchMeta がどんどん漏れる)。 - - `mincore` が Tiny ベンチ CPU の ~13% を消費。 - -### 3.2 修正内容(Phase 15) - -- free ラッパ側: - - 軽量なドメインチェックを追加: - - Tiny/Pool 用の header magic を安全に読んで、HAKMEM 所有の可能性があるものだけ `hak_free_at` へ。 - - そうでない(BenchMeta/外部)ポインタは `__libc_free` へ。 -- ExternalGuard: - - UNKNOWN ポインタを「解放しない(leak)」方針に明示的変更。 - - デバッグ時のみ `HAKMEM_EXTERNAL_GUARD_LOG=1` で原因特定用ログを出す。 - -### 3.3 結果 - -- Leak 率: - - 100K iter: 840 leaks → 0.84% - - 500K iter: ~4200 leaks → 0.84% - - ほぼ全部が BenchMeta / 外部ポインタであり、CoreAlloc 側の漏れではないと確認。 -- 性能: - - 256B 固定: - - Before: 15.9M ops/s - - After: 16.2M ops/s(+1.9%)→ domain check オーバーヘッドは軽微、むしろ微増。 -- 安定性: - - 全サイズ(128/256/512/1024B)で 500K iter 完走(クラッシュなし)。 - - ExternalGuard 経由の「危ない free」は leak に封じ込められた。 - -**要点:** -Box 境界違反(BenchMeta→CoreAlloc 流入)はほぼ完全に解消。 -ベンチでの mincore / ExternalGuard コストも許容範囲になった。 - ---- - -## 4. Phase 16: Dynamic Tiny/Mid Boundary A/B Testing(2025-11-16完了) - -### 4.1 実装内容 - -ENV変数でTiny/Mid境界を動的調整可能にする機能を追加: -- `HAKMEM_TINY_MAX_CLASS=7` (デフォルト): Tiny が 0-1023B を担当 -- `HAKMEM_TINY_MAX_CLASS=5` (実験用): Tiny が 0-255B のみ担当 - -実装ファイル: -- `hakmem_tiny.h/c`: `tiny_get_max_size()` - ENV読取とクラス→サイズマッピング -- `hakmem_mid_mt.h/c`: `mid_get_min_size()` - 動的境界調整(サイズギャップ防止) -- `hak_alloc_api.inc.h`: 静的TINY_MAX_SIZEを動的呼び出しに変更 - -### 4.2 A/B Benchmark Results - -| Size | Config A (C0-C7) | Config B (C0-C5) | 変化率 | -|------|------------------|------------------|--------| -| 128B | 6.34M ops/s | 1.38M ops/s | **-78%** ❌ | -| 256B | 6.34M ops/s | 1.36M ops/s | **-79%** ❌ | -| 512B | 5.55M ops/s | 1.33M ops/s | **-76%** ❌ | -| 1024B | 5.91M ops/s | 1.37M ops/s | **-77%** ❌ | - -### 4.3 発見と結論 - -✅ **成功**: サイズギャップ修正完了(OOMクラッシュなし) -❌ **失敗**: Tiny カバレッジ削減で大幅な性能劣化 (-76% ~ -79%) -⚠️ **根本原因**: Mid の粗いサイズクラス (8KB/16KB/32KB) が小サイズで非効率 -- Mid は 8KB ページ単位の設計 → 256B-1KB を投げると 8KB ページをほぼ数ブロックのために確保 -- ページ fault・TLB・メタデータコストが相対的に巨大 -- Tiny は slab + freelist で高密度 → 同じサイズでも桁違いに効率的 - -**教訓(ChatGPT先生分析)**: -1. Mid 箱の前提が「8KB〜用」になっている - - 256B/512B/1024B では 8KB ページをほぼ1〜数個のブロックのために確保 → 非効率 -2. パス長も Mid の方が長い(PoolTLS / mid registry / page 管理) -3. 「Tiny を削って Mid に任せれば軽くなる」という仮説は、現行の "8KB〜前提の Mid 設計" では成り立たない - -**推奨**: **デフォルト HAKMEM_TINY_MAX_CLASS=7 (C0-C7) を維持** - ---- - -## 5. Phase 17: Small-Mid Allocator Box - 実験完了 ✅(2025-11-16) - -### 5.1 目標と動機 - -**問題**: Tiny C5-C7 (256B/512B/1KB) が ~6M ops/s → system malloc の ~6.7% レベル -**仮説**: 専用層を作れば 2-4x 改善可能 -**結果**: ❌ **仮説は誤り** - 性能改善なし(±0-1%) - -### 5.2 Phase 17-1: TLS Frontend Cache(Tiny delegation) - -**実装**: -- TLS freelist(256B/512B/1KB、容量32/24/16) -- Backend: Tiny C5/C6/C7に委譲、Header変換(0xa0 → 0xb0) -- Auto-adjust: Small-Mid ON時にTinyをC0-C5に自動制限 - -**結果**: -| Size | OFF | ON | 変化率 | -|------|-----|-----|--------| -| 256B | 5.87M | 6.06M | **+3.3%** | -| 512B | 6.02M | 5.91M | **-1.9%** | -| 1024B | 5.58M | 5.54M | **-0.6%** | -| **平均** | 5.82M | 5.84M | **+0.3%** | - -**教訓**: Delegation overhead = TLS savings → 正味利益ゼロ - -### 5.3 Phase 17-2: Dedicated SuperSlab Backend - -**実装**: -- Small-Mid専用SuperSlab pool(1MB、16 slabs/SS) -- Batch refill(8-16 blocks/refill) -- 直接0xb0 header書き込み(Tiny delegationなし) - -**結果**: -| Size | OFF | ON | 変化率 | -|------|-----|-----|--------| -| 256B | 6.08M | 5.84M | **-4.1%** ⚠️ | -| 512B | 5.79M | 5.86M | **+1.2%** | -| 1024B | 5.42M | 5.44M | **+0.4%** | -| **平均** | 5.76M | 5.71M | **-0.9%** | - -**Phase 17-1比較**: Phase 17-2の方が悪化(-3.6% on 256B) - -### 5.4 根本原因分析(ChatGPT先生 + perf profiling) - -**発見**: **70% page fault** が支配的 🔥 - -**Perf分析**: -- `asm_exc_page_fault`: 70% CPU時間 -- 実際のallocation logic(TLS/refill): 30% のみ -- **結論**: Frontend実装は成功、Backendが重すぎる - -**なぜpage faultが多いか**: -``` -Small-Mid: alloc → TLS miss → refill → SuperSlab新規確保 - → mmap(1MB) → page fault 発生 → 70%のCPU消費 - -Tiny: alloc → TLS miss → refill → 既存warm SuperSlab使用 - → page faultなし → 高速 -``` - -**Small-Mid問題**: -1. 新しいSuperSlabを頻繁に確保(workloadが短いため) -2. Warm SuperSlabの再利用なし(usedカウンタ減らない) -3. Batch refillのメリットよりmmap/page faultコストが大きい - -### 5.5 Phase 17の結論と教訓 - -❌ **Small-Mid専用層戦略は失敗**: -- Phase 17-1(Frontend only): +0.3% -- Phase 17-2(Dedicated backend): -0.9% -- 目標(2-4x改善): **未達成**(-50-67%不足) - -✅ **重要な発見**: -1. **Frontend(TLS/batch refill)設計はOK** - 30%のみの負荷 -2. **70% page fault = SuperSlab層の問題** -3. **Tiny (6.08M) は既に十分速い** - これを超えるのは困難 -4. **層の分離では性能は上がらない** - Backend最適化が必要 - -✅ **実装の価値**: -- ENV=0でゼロオーバーヘッド(branch predictor学習) -- 実験記録として価値あり("なぜ専用層が効果なかったか"の証拠) -- Tiny最適化の邪魔にならない(完全分離アーキテクチャ) - -### 5.6 次のステップ: SuperSlab Reuse(Phase 18候補) - -**ChatGPT提案**: Tiny SuperSlabの最適化(Small-Mid専用層ではなく) - -**Box SS-Reuse(SuperSlab slab再利用箱)**: -- **目標**: 70% page fault → 5-10%に削減 -- **戦略**: - 1. meta->freelistを優先使用(現在はbump onlyで再利用なし) - 2. slabがemptyになったらshared_poolに返却 - 3. 同じSuperSlab内で長く回す(新規mmap削減) -- **効果**: page fault大幅削減 → 2-4x改善期待 -- **実装場所**: `core/hakmem_tiny_superslab.c`(Tiny用、Small-Midではない) - -**Box SS-Prewarm(事前温め箱)**: -- クラスごとにSuperSlabを事前確保(Phase 11実績: +6.4%) -- page faultをbenchmark開始時に集中 -- **課題**: benchmark専用、実運用では無駄 - -**推奨**: Box SS-Reuse優先(実運用価値あり、根本解決) - ---- - -## 6. 未達成の目標・残課題(次フェーズ候補) - -### 6.1 Tiny 性能ギャップ(System の ~18% 止まり) - -現状: -- System malloc が ~90M ops/s レベルのところ、 -- HAKMEM は 128〜1024B 固定で ~15–16M ops/s(約 18%)。 - -原因の切り分け(これまでの調査から): -- Front(UltraHot/TinyHeapV2/TLS SLL)のパス長はかなり短縮済み。 -- L1 dcache miss / instructions / branches は Phase 14 で大幅削減済みだが、 - - まだ Tiny が 0–1023B を全部抱えており、 - - 特に 512/1024B が Superslab/Pool 側のメタ負荷に効いている可能性。 - -候補: -- **Phase 17 で実装中!** Small-Mid Box(256B〜4KB 専用箱)を設計し、Tiny/Mid の間を分離する。 - - 詳細は § 5. Phase 17 を参照 - -### 6.2 UltraHot/TinyHeapV2 の拡張 or 整理 - -- C2/C3 UltraHot は成功(16/32B 用)。 -- C4/C5 まで拡張した試み(Phase 14-B)は: - - Fixed-size では改善あり。 - - Random Mixed で shared_pool_acquire_slab() が 47.5% まで膨らみ、大退化。 - - 原因: Superslab/TLS 在庫のバランスを壊す「窃取カスケード」。 - -方針: -- UltraHot は **C2/C3 専用 Box** に戻す(C4/C5 は一旦対象外にする)。 -- もし C4/C5 を最適化したいなら、SmallMid Box の中で別設計する。 - -### 6.3 ExternalGuard の統計と自動アラート - -- 現在: - - `HAKMEM_EXTERNAL_GUARD_STATS=1` で統計を手動出力。 - - 100+ 回呼ばれたら WARNING を出すのみ。 -- 構想: - - 「ExternalGuard 呼び出しが一定閾値を超えたら、自動で簡易レポートを吐く」Box を追加。 - - 例: Top N 呼び出し元アドレス、サイズ帯、mincore 結果 など。 - ---- - -## 7. Claude Code 君向け TODO - -### 7.1 Phase 17: Small-Mid Allocator Box ✅ 完了(2025-11-16) - -**Phase 17-1**: TLS Frontend Cache -- ✅ 実装完了(TLS freelist + Tiny delegation) -- ✅ A/B テスト: ±0.3%(性能改善なし) -- ✅ 教訓: Delegation overhead = TLS savings - -**Phase 17-2**: Dedicated SuperSlab Backend -- ✅ 実装完了(専用SuperSlab pool + batch refill) -- ✅ A/B テスト: -0.9%(Phase 17-1より悪化) -- ✅ 根本原因: 70% page fault(mmap/SuperSlab確保が重い) - -**結論**: Small-Mid専用層は性能改善なし(±0-1%)、Tiny最適化が必要 - -### 7.2 Phase 18 候補: SuperSlab Reuse(Tiny最適化) - -**Box SS-Reuse(最優先)**: -1. meta->freelist優先使用(現状: bump only) -2. slab empty検出→shared_pool返却 -3. 同じSuperSlab内で長く回す(page fault削減) -4. 目標: 70% page fault → 5-10%、性能 2-4x改善 - -**Box SS-Prewarm(次優先)**: -1. クラスごとSuperSlab事前確保 -2. page faultをbenchmark開始時に集中 -3. Phase 11実績: +6.4%(参考値) - -**Box SS-HotHint(長期)**: -1. クラス別ホットSuperSlab管理 -2. locality最適化(cache効率) -3. SS-Reuseとの統合 - -### 7.3 その他タスク - -1. ✅ **Phase 16/17 結果分析** - CURRENT_TASK.md記録完了 -2. **C2/C3 UltraHot コード掃除** - C4/C5関連を別Box化 -3. **ExternalGuard 統計自動化** - 閾値超過時レポート - ---- - -## 8. Phase 17 実装ログ(完了) - -### 2025-11-16 -- ✅ **Phase 17-1完了**: TLS Frontend + Tiny delegation - - 実装: `hakmem_smallmid.h/c`, auto-adjust, routing修正 - - A/B結果: +0.3%(性能改善なし) - - 教訓: Delegation overhead = TLS savings - -- ✅ **Phase 17-2完了**: Dedicated SuperSlab backend - - 実装: `hakmem_smallmid_superslab.h/c`, batch refill, 0xb0 header - - A/B結果: -0.9%(Phase 17-1より悪化) - - 根本原因: 70% page fault(ChatGPT + perf分析) - -- ✅ **重要な発見**: - - Frontend(TLS/batch refill): OK(30%のみ) - - Backend(SuperSlab確保): ボトルネック(70% page fault) - - 専用層では性能上がらない → **Tiny SuperSlab最適化が必要** - -- ✅ **CURRENT_TASK.md更新**: Phase 17結果 + Phase 18計画 -- 🎯 **次**: Phase 18 Box SS-Reuse実装(Tiny SuperSlab最適化) - ---- - -## 9. Phase 19 実装ログ(完了) 🎉 - -### 2025-11-16 -- ✅ **Phase 19-1完了**: Box FrontMetrics(観測) - - 実装: `core/box/front_metrics_box.h/c`、全層にヒット率計測追加 - - ENV: `HAKMEM_TINY_FRONT_METRICS=1`, `HAKMEM_TINY_FRONT_DUMP=1` - - 結果: CSV形式で per-class ヒット率レポート生成 - -- ✅ **Phase 19-2完了**: ベンチマークとヒット率分析 - - ワークロード: Random Mixed 16-1040B、50万イテレーション - - **重要な発見**: - - **HeapV2**: 88-99% ヒット率(主力として機能)✅ - - **UltraHot**: 0.2-11.7% ヒット率(ほぼ素通り)⚠️ - - FC/SFC: 無効化済み(0%) - - TLS SLL: fallback として 0.7-2.7% のみ - -- ✅ **Phase 19-3完了**: Box FrontPrune(診断) - - 実装: ENV切り替えで層を個別ON/OFF可能 - - ENV: `HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1`(デフォルトOFF) - - ENV: `HAKMEM_TINY_FRONT_DISABLE_HEAPV2=1`(デフォルトON) - -- ✅ **Phase 19-4完了**: A/Bテストと最適化 - - **テスト結果**: - | 設定 | 性能 | vs Baseline | C2/C3 ヒット率 | - |------|------|-------------|----------------| - | Baseline(両方ON) | 10.1M ops/s | - | UH=11.7%, HV2=88.3% | - | **HeapV2のみ** | **11.4M ops/s** | **+12.9%** ⭐ | HV2=99.3%, SLL=0.7% | - | UltraHotのみ | 6.6M ops/s | -34.4% ❌ | UH=96.4% (C2), SLL=94.2% (C3) | - - - **決定的結論**: - - **UltraHot削除で性能向上** (+12.9%) - - 理由: 分岐予測ミスコスト > UltraHotヒット率向上効果 - - UltraHotチェック: 88.3%のケースで無駄な分岐 → CPU分岐予測器を混乱 - - HeapV2単独の方が予測可能性が高い → 性能向上 - -- ✅ **デフォルト設定変更**: UltraHot デフォルトOFF - - 本番推奨: UltraHot OFF(最速設定) - - 研究用: `HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1` で有効化可能 - - コードは削除せず ENV切り替えで残す(研究・デバッグ用) - -- ✅ **Phase 19 成果**: - - ChatGPT先生の「観測→診断→治療」戦略が完璧に機能 🎓 - - 直感に反する発見(UltraHotが阻害要因)をデータで証明 - - A/Bテストでリスクなし確認してから最適化実施 - - 詳細: `PHASE19_FRONTEND_METRICS_FINDINGS.md`, `PHASE19_AB_TEST_RESULTS.md` - ---- - -## 10. Phase 20 計画: Tiny ホットパス一本化 + BenchFast モード 🎯 - -### 目標 -- **性能目標**: 20-30M ops/s(system malloc の 25-35%) -- **設計目標**: 「箱を崩さず」に達成(研究価値を保つ) - -### Phase 20-1: HeapV2 を唯一の Tiny Front に(本命ホットパス一本化) - -**現状認識**: -- C2/C3: HeapV2 が 88-99% を処理(本命) -- UltraHot: 0.2-11.7% しか当たらず、分岐の邪魔(削ると +12.9%) -- FC/SFC: 実質 OFF、TLS SLL は fallback のみ - -**実装方針**: -1. **HeapV2 を「唯一の front」として扱う**: - - C2-C5: HeapV2 → fallback だけ TLS SLL - - 他層(UltraHot, FC, SFC)はホットパスから完全に外し、実験用に退避 - -2. **HeapV2 の中身を徹底的に薄くする**: - - size→class 再計算を全部やめて、「class_idx を渡すだけ」にする - - 分岐を「classごとの専用関数」かテーブルジャンプにして 1-2 本に減らす - - header 書き込み・TLS stack 操作・return までを「6-8 命令の直線」に近づける - -3. **期待効果**: - - 現在 11M ops/s → 目標 15-20M ops/s (+35-80% 改善) - - 分岐削減 + 命令直線化 → CPU パイプライン効率向上 - -**ENV制御**: -```bash -# HeapV2専用モード(Phase 20デフォルト) -HAKMEM_TINY_FRONT_HEAPV2_ONLY=1 # UltraHot/FC/SFC完全バイパス - -# 旧動作(研究用) -HAKMEM_TINY_FRONT_ENABLE_ULTRAHOT=1 # Phase 19設定 -``` - ---- - -### Phase 20-2: BenchFast モードで安全コストを外す - -**現状認識**: -- `hak_free_at` / `classify_ptr` / ExternalGuard / mincore など、 - 「LD_PRELOAD / 外部ライブラリから守る」層が、 - ベンチでは「絶対に hakmem だけを使っている」前提の上に乗っている - -**実装方針**: -1. **ベンチ用完全信頼モード**(Box BenchFast): - - alloc/free ともに: - - header 1バイト で Tiny を即判定 - - Pool/Mid/L25/ExternalGuard/registry を完全にバイパス - - 変なポインタが来たら壊れていい(ベンチ用なので) - -2. **ENV制御**: - ```bash - HAKMEM_BENCH_FAST_MODE=1 # 安全コスト全外し - ``` - -3. **目的**: - - 「箱全部乗せ版」と「安全コスト全外し版」の差を測る - - 「設計そのものの限界」と「安全・汎用性のコスト」の内訳を見る - - mimalloc と同じくらい「危ないモード」で、どこまで近づけるかを研究 - -4. **期待効果**: - - HeapV2専用モード: 15-20M ops/s - - BenchFast追加: 25-30M ops/s (+65-100% vs 現状) - - system malloc (90M ops/s) の 28-33% に到達 - ---- - -### Phase 20-3: SuperSlab ホットセット チューニング - -**現状認識**: -- SS-Reuse: 再利用率 98.8%、新規 mmap 1.2% → page fault は抑えられている -- とはいえ perf ではまだ `asm_exc_page_fault` がでかく見える場面もある - -**実装方針**: -1. **Box SS-HotSet**(どのクラスが何枚をホットに持つか計測): - - クラスごとの「ホット SuperSlab 数」を 1-2 枚に抑えるように class_hints をチューニング - - precharge (`HAKMEM_TINY_SS_PRECHARGE_Cn`) を使って、「最初から 2 枚だけ温める」戦略を試す - -2. **Box SS-Compact**(ホットセット圧縮): - - 同じ SuperSlab に複数のホットクラスを詰め込む(Phase 12 の発展) - - 例: C2/C3 を同じ SuperSlab に配置 → キャッシュ効率向上 - -3. **期待効果**: - - page fault さらに削減 → +10-20% 性能向上 - - 既存の SS-Reuse/Cache 設計を、「Tiny front が見ているサイズ帯に合わせて細かく調整」 - ---- - -### Phase 20 実装順序 - -1. **Phase 20-1**: HeapV2 専用モード実装(優先度: 高) - - 期待: +35-80% (11M → 15-20M ops/s) - - 工数: 中(既存 HeapV2 をスリム化) - -2. **Phase 20-2**: BenchFast モード実装(優先度: 中) - - 期待: +65-100% (11M → 25-30M ops/s) - - 工数: 中(安全層バイパス) - -3. **Phase 20-3**: SS-HotSet チューニング(優先度: 低) - - 期待: +10-20% 追加改善 - - 工数: 小(パラメータ調整 + 計測箱追加) - ---- - -### Phase 20 成功条件 - -- ✅ Tiny 固定サイズで 20-30M ops/s 達成(system の 25-35%) -- ✅ 「箱を崩さず」達成(研究箱としての価値を保つ) -- ✅ ENV切り替えで「安全モード」「ベンチモード」を選べる状態を維持 -- ✅ 残りの差(system との 2.5-3x)は「kernel/page fault + mimalloc の極端な inlining」と言える根拠を固める - ---- - -### Phase 20 後の展望 - -ここまで行けたら: -- 「残りの差は kernel/page fault + mimalloc の極端な inlining・OS依存の差」だと自信を持って言える -- hakmem の「研究箱」としての価値(構造をいじりやすい / 可視化しやすい)を保ったまま、 - 性能面でも「そこそこ実用に耐える」ラインに乗る -- 学術論文・技術ブログでの発表材料が揃う - ---- - -## 11. Phase 20-1 実装ログ: Box SS-HotPrewarm(TLS Cache 事前確保) ✅ - -### 2025-11-16 - -#### 実装内容 -- ✅ **Box SS-HotPrewarm 作成**: ENV制御の per-class TLS cache prewarm - - 実装: `core/box/ss_hot_prewarm_box.h/c` - - デフォルト targets: C2/C3=128, C4/C5=64(aggressive prewarm) - - ENV制御: `HAKMEM_TINY_PREWARM_C2`, `_C3`, `_C4`, `_C5`, `_ALL` - -- ✅ **初期化統合**: `hak_init_impl()` から自動呼び出し - - 384 ブロック事前確保(C2=128, C3=128, C4=64, C5=64) - - `box_prewarm_tls()` API 使用(安全な carve-push) - -#### ベンチマーク結果(500K iterations, 256B random mixed) - -| 設定 | Page Faults | Throughput | vs Baseline | -|------|-------------|------------|-------------| -| **Baseline** (Prewarm OFF) | 10,399 | 15.7M ops/s | - | -| **Phase 20-1** (Prewarm ON) | 10,342 | 16.2M ops/s | **+3.3%** ⭐ | - -- **Page fault 削減**: 0.55%(期待: 50-66% → 現実: ほぼなし) -- **性能向上**: +3.3%(15.7M → 16.2M ops/s) - -#### 分析と結論 - -**❌ Page Fault 削減の失敗理由**: -1. **ユーザーページ由来が支配的**: ベンチマーク自体の初期化・データ構造確保による page fault が大半 -2. **SuperSlab 事前確保の限界**: 384 ブロック程度の prewarm では、ベンチマーク全体の page fault (10K+) に対して微々たる影響しかない -3. **カーネル側のコスト**: `asm_exc_page_fault` はユーザー空間だけでは制御不可能 - -**✅ Cache Warming 効果**: -1. **TLS SLL 事前充填**: 初期の refill コスト削減 -2. **CPU サイクル節約**: +3.3% の性能向上 -3. **安定性向上**: 初期状態が warm → 最初のアロケーションから高速 - -#### 決定: 「軽い +3% 箱」として確定 - -- **prewarm は有効**: 384 ブロック確保(C2/C3=128, C4/C5=64)のまま残す -- **これ以上の aggressive 化は不要**: RSS 消費増 vs page fault 削減効果が見合わない -- **次フェーズへ**: BenchFast モードで「上限性能」を測定し、構造的限界を把握 - -#### 変更ファイル -- `core/box/ss_hot_prewarm_box.h` - NEW -- `core/box/ss_hot_prewarm_box.c` - NEW -- `core/box/hak_core_init.inc.h` - prewarm 呼び出し追加 -- `Makefile` - `ss_hot_prewarm_box.o` 追加 - ---- - -**Status**: Phase 20-1 完了 ✅ → **Phase 20-2 準備中** 🎯 -**Next**: BenchFast モード実装(安全コスト全外し → 構造的上限測定) - - ---- - -## Phase 20-2: BenchFast Mode Implementation (2025-11-16) ✅ - -**Status**: ✅ **COMPLETE** - Recursion fixed via prealloc pool + init guard -**Goal**: Measure HAKMEM's structural performance ceiling by removing ALL safety costs -**Implementation**: Complete (core/box/bench_fast_box.{h,c}) - -### Design Philosophy - -BenchFast mode bypasses all safety mechanisms to measure the theoretical maximum throughput: - -**Alloc path** (6-8 instructions): -- size → class_idx → TLS SLL pop → write header → return USER pointer -- Bypasses: classify_ptr, Pool/Mid routing, registry, refill logic - -**Free path** (3-5 instructions): -- Read header → BASE pointer → TLS SLL push -- Bypasses: registry lookup, mincore, ExternalGuard, capacity checks - -### Implementation Details - -**Files Created**: -- `core/box/bench_fast_box.h` - ENV-gated API with recursion guard -- `core/box/bench_fast_box.c` - Ultra-minimal alloc/free + prealloc pool - -**Integration**: -- `core/box/hak_wrappers.inc.h` - malloc()/free() wrappers with BenchFast bypass -- `bench_random_mixed.c` - bench_fast_init() call before benchmark loop -- `Makefile` - bench_fast_box.o added to all object lists - -**Activation**: -```bash -export HAKMEM_BENCH_FAST_MODE=1 -./bench_fixed_size_hakmem 500000 256 128 -``` - -### Recursion Fix: Prealloc Pool Strategy - -**Problem**: When TLS SLL is empty, bench_fast_alloc() → hak_alloc_at() → malloc() → infinite loop - -**Solution** (User's "C案"): -1. **Prealloc pool**: bench_fast_init() pre-allocates 50K blocks per class using normal path -2. **Init guard**: `bench_fast_init_in_progress` flag prevents BenchFast during init -3. **Pop-only alloc**: bench_fast_alloc() only pops from pool, NO REFILL - -**Key Fix** (User's contribution): -```c -// core/box/bench_fast_box.h -extern __thread int bench_fast_init_in_progress; - -// core/box/hak_wrappers.inc.h (malloc wrapper) -if (__builtin_expect(!bench_fast_init_in_progress && bench_fast_enabled(), 0)) { - return bench_fast_alloc(size); // Only activate AFTER init complete -} -``` - -### Performance Results (500K iterations, 256B fixed-size) - -| Mode | Throughput | vs Baseline | vs System | -|------|------------|-------------|-----------| -| **Baseline** (通常) | 54.4M ops/s | - | 53.3% | -| **BenchFast** (安全コスト除去) | 56.9M ops/s | **+4.5%** | 55.7% | -| **System malloc** | 102.1M ops/s | +87.6% | 100% | - -### 🔍 Critical Discovery: Safety Costs Are NOT the Bottleneck - -**BenchFast で安全コストをすべて除去しても、わずか +4.5% しか改善しない!** - -**What this reveals**: -- classify_ptr、Pool/Mid routing、registry、mincore、ExternalGuard → これらは**ボトルネックではない** -- 本当のボトルネックは**構造的な部分**: - - SuperSlab 設計(1 SS = 1 class 固定) - - メタデータアクセスパターン(cache miss 多発) - - TLS SLL 効率(pointer chasing overhead) - - 877 SuperSlab 生成による巨大なメタデータフットプリント - -**System malloc との差**: -- Baseline: 47.7M ops/s 遅い(-46.7%) -- BenchFast でも 45.2M ops/s 遅い(-44.3%) -- → 安全コスト除去しても差は **たった 2.5M ops/s しか縮まらない** - -### Implications for Future Work - -**増分最適化の限界**: -- Phase 9-11 で学んだ教訓を確認:症状の緩和では埋まらない -- 安全コストは全体の 4.5% しか占めていない -- 残り 95.5% は**構造的なボトルネック** - -**Phase 12 Shared SuperSlab Pool の重要性**: -- 877 SuperSlab → 100-200 に削減 -- メタデータフットプリント削減 → cache miss 削減 -- 動的 slab 共有 → 使用効率向上 -- 期待性能: 70-90M ops/s(System の 70-90%) - -### Bottleneck Breakdown (推定) - -| コンポーネント | CPU 時間 | BenchFast で除去? | -|---------------|----------|------------------| -| SuperSlab metadata access | ~35% | ❌ 構造的 | -| TLS SLL pointer chasing | ~25% | ❌ 構造的 | -| Refill + carving | ~15% | ❌ 構造的 | -| classify_ptr + registry | ~10% | ✅ 除去済み | -| Pool/Mid routing | ~5% | ✅ 除去済み | -| mincore + guards | ~5% | ✅ 除去済み | -| その他 | ~5% | - | - -**結論**: 構造的ボトルネック(75%)>> 安全コスト(20%) - -**Next Steps**: -- Phase 12: Shared SuperSlab Pool(本質的解決) -- 877 SuperSlab → 100-200 に削減して cache miss を大幅削減 -- 期待性能: 70-90M ops/s(System の 70-90%) - -**Phase 20 完了**: BenchFast モードで「安全コストは 4.5%」と証明 ✅ - ---- - -## Phase 21: Hot Path Cache Optimization (HPCO) - 構造的ボトルネック攻略 🎯 - -**Status**: 🚧 **PLANNING** (ChatGPT先生のフィードバック反映済み) -**Goal**: アクセスパターン最適化で 60% CPU(メタアクセス 35% + ポインタチェイス 25%)を直接攻撃 -**Target**: 75-82M ops/s(System malloc の 73-80%) - -### Phase 20-2 で判明した構造的ボトルネック - -**BenchFast の結論**: -- 安全コスト(classify_ptr/Pool routing/registry/mincore/guards)= **4.5%** しかない -- 残り 45M ops/s の差 = **箱の積み方そのもの** - -**支配的ボトルネック** (60% CPU): -``` -メタアクセス: ~35% (SuperSlab/TinySlabMeta の複数フィールド読み書き) -ポインタチェイス: ~25% (TLS SLL の next ポインタたどり) -carve/refill: ~15% (batch carving + metadata updates) -``` - -**1 回の alloc/free で発生すること**: -- 何段も構造体を跨ぐ(TLS → SuperSlab → SlabMeta → freelist) -- ポインタを何回もたどる(SLL の next チェイン) -- メタデータを何フィールドも触る(used/capacity/carved/freelist/...) - -### Phase 21 戦略(ChatGPT先生フィードバック反映) - -#### Phase 21-1: Array-Based TLS Cache (C2/C3) 🔴 最優先 - -**狙い**: TLS SLL のポインタチェイス削減 → +15-20% - -**現状の問題**: -```c -// TLS SLL (linked list) - 3 メモリアクセス、うち 1 回は cache miss -void* ptr = g_tls_sll_head[class_idx]; // 1. ヘッド読み込み -void* next = *(void**)ptr; // 2. next ポインタ読み込み (cache miss!) -g_tls_sll_head[class_idx] = next; // 3. ヘッド更新 -``` - -**解決策: Ring Buffer**: -```c -// Box 21-1: Array-based hot cache (C2/C3 only) -typedef struct { - void* slots[128]; // 初期サイズ 128(ENV で A/B: 64/128/256) - uint16_t head; // pop index - uint16_t tail; // push index -} TlsRingCache; - -static __thread TlsRingCache g_hot_cache_c2; -static __thread TlsRingCache g_hot_cache_c3; - -// Ultra-fast alloc (1-2 命令) -void* ptr = g_hot_cache_c2.slots[g_hot_cache_c2.head++ & 0x7F]; // ring wrap -``` - -**階層化** (ChatGPT先生フィードバック): -``` -Ring → SLL → SuperSlab - ↑ ↑ ↑ - L0 L1 L2 - -- alloc: Ring → 空なら SLL → 空なら SuperSlab -- free: Ring → 満杯なら SLL -- drain: SLL → Ring に昇格(一方向) -``` - -**効果**: -- ポインタチェイス: 1 回 → **0 回** -- メモリアクセス: 3 → **2 回** -- cache locality: 配列は連続メモリ -- **期待: +15-20%** (54.4M → 62-65M ops/s) - -**ENV 変数**: -```bash -HAKMEM_TINY_HOT_RING_C2=128 # C2 Ring サイズ (default: 128) -HAKMEM_TINY_HOT_RING_C3=128 # C3 Ring サイズ (default: 128) -HAKMEM_TINY_HOT_RING_ENABLE=1 # Ring cache 有効化 -``` - -**実装ポイント** (ChatGPT先生): -- Ring サイズは 64/128/256 で A/B テスト -- C0/C1/C4/C5/C6/C7 は SLL のまま(使用頻度低い) -- drain 時: SLL → Ring への昇格(一方向) -- Ring が空 → SLL fallback → SuperSlab refill - -#### Phase 21-2: Hot Slab Direct Index 🟡 中優先度 - -**狙い**: SuperSlab → slab ループ削減 → +10-15% - -**現状の問題**: -```c -// 毎回 32 slab をスキャン -SuperSlab* ss = g_tls_slabs[class_idx].ss; -for (int i = 0; i < 32; i++) { // ← ループ! - TinySlabMeta* meta = &ss->slabs[i]; - if (meta->freelist != NULL) { ... } -} -``` - -**解決策: Hot Slab Cache**: -```c -// Box 21-2: Direct index to hot slab -static __thread TinySlabMeta* g_hot_slab[TINY_NUM_CLASSES]; - -void refill_from_hot_slab(int class_idx) { - TinySlabMeta* hot = g_hot_slab[class_idx]; - - // Hot slab が空なら更新 - if (!hot || hot->freelist == NULL) { - hot = find_nonempty_slab(class_idx); // 1回だけ探索 - g_hot_slab[class_idx] = hot; // cache! - } - - pop_batch_from_freelist(hot, ...); // no loop! -} -``` - -**効果**: -- SuperSlab → slab ループ: 削除 -- メタアクセス: 32 回 → **1 回** -- **期待: +10-15%** (62-65M → 70-75M ops/s) - -**実装ポイント** (ChatGPT先生): -- Hot slab が EMPTY → find_nonempty_slab で差し替え -- free 時: hot slab に返す or freelist に戻す(ポリシー決める) -- shared_pool / SS-Reuse との整合性確保 - -#### Phase 21-3: Minimal Meta Access (C2/C3) 🟢 低優先度 - -**狙い**: 触るフィールド削減 → +5-10% - -**現状の問題**: -```c -// 1 alloc/free で 4-5 フィールド触る -typedef struct { - uint16_t used; // ✅ 必須 - uint16_t capacity; // ❌ compile-time 定数化できる - uint16_t carved; // ❌ C2/C3 では使わない - void* freelist; // ✅ 必須 -} TinySlabMeta; -``` - -**解決策: アクセスパターン限定** (ChatGPT先生): -```c -// struct を分けなくてもOK(型分岐を避ける) -// C2/C3 コードパスで触るのを used/freelist だけに限定 - -#define C2_CAPACITY 64 // compile-time 定数 - -static inline int c2_can_alloc(TinySlabMeta* meta) { - return meta->used < C2_CAPACITY; // capacity フィールド不要! -} -``` - -**効果**: -- 触るフィールド: 4-5 → **2 個** (used/freelist のみ) -- cache line 消費: 削減 -- **期待: +5-10%** (70-75M → 75-82M ops/s) - -**実装ポイント** (ChatGPT先生): -- struct 分離は後回し(型分岐コスト vs 効果のトレードオフ) -- アクセスパターン限定だけでも cache 効果あり -- Phase 21-1/2 の結果を見てから判断 - -### Phase 21 実装順序 - -``` -Phase 21-1 (Array-based TLS Cache C2/C3): - ↓ +15-20% → 62-65M ops/s -Phase 21-2 (Hot Slab Direct Index): - ↓ +10-15% → 70-75M ops/s -Phase 21-3 (Minimal Meta Access): - ↓ +5-10% → 75-82M ops/s - ↓ -🎯 Target: System malloc の 73-80% -``` - -**Phase 12 (SuperSlab 共有) は後回し**: -- Phase 21 で 80M ops/s 到達後、残り 20M ops/s を Phase 12 で詰める - -### ChatGPT先生フィードバック(重要) - -1. **Box 21-1 (Ring cache)**: ✅ perf 的にドンピシャ - - Ring → SLL → SuperSlab の階層を明確に - - Ring サイズは 128/64 から ENV で A/B - - drain 時: SLL → Ring への昇格(一方向) - -2. **Box 21-2 (Hot slab)**: ✅ 有効だが扱いに注意 - - hot slab が EMPTY 時の差し替えロジック - - shared_pool / SS-Reuse との整合性 - -3. **Box 21-3 (Minimal meta)**: ⚠️ 後回しでOK - - struct 分離は型分岐コスト増 - - アクセスパターン限定だけで効果あり - - 21-1/2 の結果を見てから判断 - -4. **Phase 12 との順番**: ✅ 合理的 - - アクセスパターン > SuperSlab 数 - - Phase 21 → Phase 12 の順で問題なし - -### 実装リスク - -**低リスク**: -- C2/C3 のみ変更(他クラスは SLL のまま) -- 既存構造を大きく変えない -- ENV で A/B テスト可能 - -**注意点**: -- Ring と SLL の境界を明確に -- shared_pool / SS-Reuse との整合 -- 型分岐が増えすぎないように - -### 次のアクション - -**Phase 21-1 実装開始**: -1. `core/box/hot_ring_cache_box.{h,c}` 作成 -2. C2/C3 専用 TlsRingCache 実装 -3. Ring → SLL → SuperSlab 階層化 -4. ENV: `HAKMEM_TINY_HOT_RING_ENABLE=1` -5. ベンチマーク: 目標 62-65M ops/s (+15-20%) - ---- - - ---- - -## HAKMEM ハング問題調査 (2025-11-16) - -### 症状 -1. `bench_fixed_size_hakmem 1 16 128` → 5秒以上ハング -2. `bench_random_mixed_hakmem 500000 256 42` → キルされた - -### Root Cause -**Cross-thread check の always-on 化** (直前の修正) -- `core/tiny_free_fast_v2.inc.h:175-204` で ENV ゲート削除 -- Single-thread でも毎回 SuperSlab lookup 実行 - -### ハング箇所の推定 (確度順) - -| 箇所 | ファイル:行 | 原因 | 確度 | -|------|-----------|------|------| -| `hak_super_lookup()` registry probing | `core/hakmem_super_registry.h:119-187` | 線形探索 32-64 iterations / free | **高** | -| Node pool exhausted fallback | `core/hakmem_shared_pool.c:394-400` | sp_freelist_push_lockfree fallback の unsafe | 中 | -| `tls_sll_push()` CAS loop | `core/box/tls_sll_box.h:75-184` | 単純実装、無限ループはなさそう | 低 | - -### パフォーマンス影響 - -``` -Before (header-based): 5-10 cycles/free -After (cross-thread): 110-520 cycles/free (11-51倍遅い!) - -500K iterations: - 500K × 200 cycles = 100M cycles @ 3GHz = 33ms - → Overhead は大きいが単なる遅さ? -``` - -### Node pool exhausted の真実 - -- `MAX_FREE_NODES_PER_CLASS = 4096` -- 500K iterations > 4096 → exhausted ⚠️ -- しかし fallback (`sp_freelist_push()`) は lock-free で安全 -- **副作用であり、直接的ハング原因ではない可能性高い** - -### 推奨修正 - -✅ **ENV ゲートで cross-thread check を復活** -```c -// core/tiny_free_fast_v2.inc.h:175 -static int g_larson_fix = -1; -if (__builtin_expect(g_larson_fix == -1, 0)) { - const char* e = getenv("HAKMEM_TINY_LARSON_FIX"); - g_larson_fix = (e && *e && *e != '0') ? 1 : 0; -} - -if (__builtin_expect(g_larson_fix, 0)) { - // Cross-thread check - only for MT - SuperSlab* ss = hak_super_lookup(base); - // ... rest of check -} -``` - -**利点:** -- Single-thread ベンチ: 5-10 cycles (fast) -- Larson MT: `HAKMEM_TINY_LARSON_FIX=1` で有効 (safe) - -### 検証コマンド - -```bash -# 1. ハング確認 -timeout 5 ./out/release/bench_fixed_size_hakmem 1 16 128 -echo $? # 124 = timeout - -# 2. 修正後確認 -HAKMEM_TINY_LARSON_FIX=0 ./out/release/bench_fixed_size_hakmem 1 16 128 -# Should complete fast - -# 3. 500K テスト -./out/release/bench_random_mixed_hakmem 500000 256 42 2>&1 | grep "Node pool" -# Output: [P0-4 WARN] Node pool exhausted for class 7 -``` - -### 詳細レポート -- **HANG分析**: `/tmp/HAKMEM_HANG_INVESTIGATION_FINAL.md` diff --git a/core/box/free_local_box.c b/core/box/free_local_box.c index d10125ea..211b53db 100644 --- a/core/box/free_local_box.c +++ b/core/box/free_local_box.c @@ -2,6 +2,7 @@ #include "free_publish_box.h" #include "hakmem_tiny.h" #include "tiny_next_ptr_box.h" // Phase E1-CORRECT: Box API +#include "ss_hot_cold_box.h" // Phase 12-1.1: EMPTY slab marking void tiny_free_local_box(SuperSlab* ss, int slab_idx, TinySlabMeta* meta, void* ptr, uint32_t my_tid) { extern _Atomic uint64_t g_free_local_box_calls; @@ -105,15 +106,21 @@ void tiny_free_local_box(SuperSlab* ss, int slab_idx, TinySlabMeta* meta, void* meta->used--; ss_active_dec_one(ss); - // DEBUG LOGGING - Track when used reaches 0 - static int dbg = -1; - if (__builtin_expect(dbg == -1, 0)) { - const char* e = getenv("HAKMEM_SS_FREE_DEBUG"); - dbg = (e && *e && *e != '0') ? 1 : 0; - } - if (dbg == 1 && meta->used == 0) { - fprintf(stderr, "[FREE_LOCAL_BOX] meta->used=0 detected: cls=%u ss=%p slab=%d\n", - cls, (void*)ss, slab_idx); + // Phase 12-1.1: EMPTY slab detection (immediate reuse optimization) + if (meta->used == 0) { + // Slab became EMPTY → mark for highest-priority reuse + ss_mark_slab_empty(ss, slab_idx); + + // DEBUG LOGGING - Track when used reaches 0 + static int dbg = -1; + if (__builtin_expect(dbg == -1, 0)) { + const char* e = getenv("HAKMEM_SS_FREE_DEBUG"); + dbg = (e && *e && *e != '0') ? 1 : 0; + } + if (dbg == 1) { + fprintf(stderr, "[FREE_LOCAL_BOX] EMPTY detected: cls=%u ss=%p slab=%d empty_mask=0x%x empty_count=%u\n", + cls, (void*)ss, slab_idx, ss->empty_mask, ss->empty_count); + } } if (prev == NULL) { diff --git a/core/box/free_local_box.d b/core/box/free_local_box.d index 21ec4158..86971989 100644 --- a/core/box/free_local_box.d +++ b/core/box/free_local_box.d @@ -6,7 +6,8 @@ core/box/free_local_box.o: core/box/free_local_box.c \ core/hakmem_tiny_superslab_constants.h core/box/free_publish_box.h \ core/hakmem_tiny.h core/hakmem_trace.h core/hakmem_tiny_mini_mag.h \ core/box/tiny_next_ptr_box.h core/hakmem_tiny_config.h \ - core/tiny_nextptr.h + core/tiny_nextptr.h core/box/ss_hot_cold_box.h \ + core/box/../superslab/superslab_types.h core/box/free_local_box.h: core/hakmem_tiny_superslab.h: core/superslab/superslab_types.h: @@ -24,3 +25,5 @@ core/hakmem_tiny_mini_mag.h: core/box/tiny_next_ptr_box.h: core/hakmem_tiny_config.h: core/tiny_nextptr.h: +core/box/ss_hot_cold_box.h: +core/box/../superslab/superslab_types.h: diff --git a/core/box/ss_hot_cold_box.h b/core/box/ss_hot_cold_box.h index d60960bb..2bdafa60 100644 --- a/core/box/ss_hot_cold_box.h +++ b/core/box/ss_hot_cold_box.h @@ -32,6 +32,12 @@ // Phase 3d-C: Hot/Cold判定閾値 #define HOT_UTILIZATION_THRESHOLD 50 // 使用率50%以上でホット判定 +// Phase 12-1.1: EMPTY判定ロジック(最優先再利用) +// Returns: true if slab is completely EMPTY (used == 0, highest reuse priority) +static inline bool ss_is_slab_empty(const TinySlabMeta* meta) { + return (meta->capacity > 0 && meta->used == 0); +} + // Phase 3d-C: Hot判定ロジック // Returns: true if slab is "hot" (high utilization, should be prioritized) static inline bool ss_is_slab_hot(const TinySlabMeta* meta) { @@ -43,6 +49,30 @@ static inline bool ss_is_slab_hot(const TinySlabMeta* meta) { return (meta->used * 100 / meta->capacity) > HOT_UTILIZATION_THRESHOLD; } +// Phase 12-1.1: EMPTY mask更新ヘルパー +// Marks a slab as EMPTY (highest reuse priority) +static inline void ss_mark_slab_empty(SuperSlab* ss, int slab_idx) { + if (!ss || slab_idx < 0 || slab_idx >= SLABS_PER_SUPERSLAB_MAX) return; + + uint32_t bit = (1u << slab_idx); + if (!(ss->empty_mask & bit)) { + ss->empty_mask |= bit; + ss->empty_count++; + } +} + +// Phase 12-1.1: EMPTY mask クリアヘルパー +// Removes a slab from EMPTY state (when reactivated) +static inline void ss_clear_slab_empty(SuperSlab* ss, int slab_idx) { + if (!ss || slab_idx < 0 || slab_idx >= SLABS_PER_SUPERSLAB_MAX) return; + + uint32_t bit = (1u << slab_idx); + if (ss->empty_mask & bit) { + ss->empty_mask &= ~bit; + ss->empty_count--; + } +} + // Phase 3d-C: Hot/Cold インデックス更新 // Rebuilds hot_indices[] and cold_indices[] arrays based on current slab state static inline void ss_update_hot_cold_indices(SuperSlab* ss) { @@ -50,13 +80,16 @@ static inline void ss_update_hot_cold_indices(SuperSlab* ss) { ss->hot_count = 0; ss->cold_count = 0; + // Phase 12-1.1: Reset empty tracking + ss->empty_mask = 0; + ss->empty_count = 0; uint32_t max_slabs = (1u << ss->lg_size) / SLAB_SIZE; if (max_slabs > SLABS_PER_SUPERSLAB_MAX) { max_slabs = SLABS_PER_SUPERSLAB_MAX; } - // Scan active slabs and classify as hot or cold + // Scan active slabs and classify as EMPTY / hot / cold for (uint32_t i = 0; i < max_slabs && i < ss->active_slabs; i++) { TinySlabMeta* meta = &ss->slabs[i]; @@ -65,6 +98,12 @@ static inline void ss_update_hot_cold_indices(SuperSlab* ss) { continue; } + // Phase 12-1.1: EMPTY slabs have highest reuse priority + if (ss_is_slab_empty(meta)) { + ss_mark_slab_empty(ss, (int)i); + continue; // Don't add to hot/cold arrays + } + if (ss_is_slab_hot(meta)) { // Hot slab: high utilization if (ss->hot_count < 16) { @@ -85,6 +124,9 @@ static inline void ss_init_hot_cold(SuperSlab* ss) { ss->hot_count = 0; ss->cold_count = 0; + // Phase 12-1.1: Initialize EMPTY tracking + ss->empty_mask = 0; + ss->empty_count = 0; // Initialize index arrays to 0 (defensive programming) for (int i = 0; i < 16; i++) { diff --git a/core/hakmem_shared_pool.c b/core/hakmem_shared_pool.c index 94630547..ee5f3798 100644 --- a/core/hakmem_shared_pool.c +++ b/core/hakmem_shared_pool.c @@ -2,6 +2,7 @@ #include "hakmem_tiny_superslab.h" #include "hakmem_tiny_superslab_constants.h" #include "box/ss_slab_meta_box.h" // Phase 3d-A: SlabMeta Box boundary +#include "box/ss_hot_cold_box.h" // Phase 12-1.1: EMPTY slab marking #include "box/pagefault_telemetry_box.h" // Box PageFaultTelemetry (PF_BUCKET_SS_META) #include "box/tls_sll_drain_box.h" // Box TLS SLL Drain (tiny_tls_sll_drain) #include "hakmem_policy.h" // FrozenPolicy (learning layer) @@ -770,6 +771,65 @@ shared_pool_acquire_slab(int class_idx, SuperSlab** ss_out, int* slab_idx_out) } stage1_retry_after_tension_drain: + // ========== Stage 0.5 (NEW - Phase 12-1.1): EMPTY slab direct scan ========== + // Scan existing SuperSlabs for EMPTY slabs (highest reuse priority) + // This avoids Stage 3 (mmap) when freed slabs are available + // ENV: HAKMEM_SS_EMPTY_REUSE=1 to enable (default OFF for A/B testing) + static int empty_reuse_enabled = -1; + if (__builtin_expect(empty_reuse_enabled == -1, 0)) { + const char* e = getenv("HAKMEM_SS_EMPTY_REUSE"); + empty_reuse_enabled = (e && *e && *e != '0') ? 1 : 0; // default OFF + } + + if (empty_reuse_enabled) { + extern SuperSlab* g_super_reg_by_class[TINY_NUM_CLASSES][SUPER_REG_PER_CLASS]; // from hakmem_super_registry.h + extern int g_super_reg_class_size[TINY_NUM_CLASSES]; + + int reg_size = (class_idx < TINY_NUM_CLASSES) ? g_super_reg_class_size[class_idx] : 0; + static int scan_limit = -1; + if (__builtin_expect(scan_limit == -1, 0)) { + const char* e = getenv("HAKMEM_SS_EMPTY_SCAN_LIMIT"); + scan_limit = (e && *e) ? atoi(e) : 16; // default: scan first 16 SuperSlabs + } + if (scan_limit > reg_size) scan_limit = reg_size; + + for (int i = 0; i < scan_limit; i++) { + SuperSlab* ss = g_super_reg_by_class[class_idx][i]; + if (!(ss && ss->magic == SUPERSLAB_MAGIC)) continue; + if (ss->empty_count == 0) continue; // No EMPTY slabs in this SS + + // Found SuperSlab with EMPTY slabs - scan empty_mask + uint32_t mask = ss->empty_mask; + while (mask) { + int empty_idx = __builtin_ctz(mask); + mask &= (mask - 1); // clear lowest bit + + // Validate this slab is truly EMPTY and reusable + TinySlabMeta* meta = &ss->slabs[empty_idx]; + if (meta->capacity > 0 && meta->used == 0) { + // Clear EMPTY state (will be re-marked on next free) + ss_clear_slab_empty(ss, empty_idx); + + // Bind this slab to class_idx + meta->class_idx = (uint8_t)class_idx; + + if (dbg_acquire == 1) { + fprintf(stderr, "[SP_ACQUIRE_STAGE0.5_EMPTY] class=%d reusing EMPTY slab (ss=%p slab=%d empty_count=%u)\n", + class_idx, (void*)ss, empty_idx, ss->empty_count); + } + + *ss_out = ss; + *slab_idx_out = empty_idx; + sp_stage_stats_init(); + if (g_sp_stage_stats_enabled) { + atomic_fetch_add(&g_sp_stage1_hits[class_idx], 1); // Count as Stage 1 hit + } + return 0; // ✅ Stage 0.5 (EMPTY scan) success + } + } + } + } + // ========== Stage 1 (Lock-Free): Try to reuse EMPTY slots ========== // P0-4: Lock-free pop from per-class free list (no mutex needed!) // Best case: Same class freed a slot, reuse immediately (cache-hot) diff --git a/core/hakmem_tiny.d b/core/hakmem_tiny.d index ca7012f2..4bc25124 100644 --- a/core/hakmem_tiny.d +++ b/core/hakmem_tiny.d @@ -22,7 +22,10 @@ core/hakmem_tiny.o: core/hakmem_tiny.c core/hakmem_tiny.h \ core/tiny_ready_bg.h core/tiny_route.h core/box/adopt_gate_box.h \ core/tiny_tls_guard.h core/hakmem_tiny_tls_list.h \ core/hakmem_tiny_bg_spill.h core/tiny_adaptive_sizing.h \ - core/tiny_system.h core/hakmem_prof.h core/front/quick_slot.h \ + core/tiny_system.h core/hakmem_prof.h core/hakmem_tiny_config_box.inc \ + core/hakmem_tiny_ss_active_box.inc core/hakmem_tiny_globals_box.inc \ + core/hakmem_tiny_publish_box.inc core/hakmem_tiny_legacy_slow_box.inc \ + core/hakmem_tiny_tls_state_box.inc core/front/quick_slot.h \ core/front/../hakmem_tiny.h core/front/fast_cache.h \ core/front/quick_slot.h core/front/../hakmem_tiny_fastcache.inc.h \ core/front/../hakmem_tiny.h core/front/../tiny_remote.h \ @@ -37,22 +40,26 @@ core/hakmem_tiny.o: core/hakmem_tiny.c core/hakmem_tiny.h \ core/hakmem_tiny_hotmag.inc.h core/hakmem_tiny_hot_pop.inc.h \ core/hakmem_tiny_refill.inc.h core/tiny_box_geometry.h \ core/tiny_region_id.h core/hakmem_tiny_ultra_front.inc.h \ - core/hakmem_tiny_intel.inc core/hakmem_tiny_background.inc \ - core/hakmem_tiny_bg_bin.inc.h core/hakmem_tiny_tls_ops.h \ - core/hakmem_tiny_remote.inc core/hakmem_tiny_init.inc \ + core/hakmem_tiny_intel.inc core/hakmem_tiny_eventq_box.inc \ + core/hakmem_tiny_background.inc core/hakmem_tiny_bg_bin.inc.h \ + core/hakmem_tiny_tls_ops.h core/hakmem_tiny_sll_cap_box.inc \ + core/hakmem_tiny_ultra_batch_box.inc core/hakmem_tiny_remote.inc \ + core/hakmem_tiny_slab_lookup_box.inc core/hakmem_tiny_init.inc \ core/box/prewarm_box.h core/hakmem_tiny_bump.inc.h \ - core/hakmem_tiny_smallmag.inc.h core/tiny_atomic.h \ - core/tiny_alloc_fast.inc.h core/tiny_alloc_fast_sfc.inc.h \ - core/hakmem_tiny_fastcache.inc.h core/box/front_metrics_box.h \ - core/hakmem_tiny_lazy_init.inc.h core/box/tiny_sizeclass_hist_box.h \ - core/tiny_alloc_fast_inline.h core/tiny_free_fast.inc.h \ - core/hakmem_tiny_alloc.inc core/hakmem_tiny_slow.inc \ - core/hakmem_tiny_free.inc core/box/free_publish_box.h core/mid_tcache.h \ + core/hakmem_tiny_smallmag.inc.h core/hakmem_tiny_phase6_wrappers_box.inc \ + core/tiny_atomic.h core/tiny_alloc_fast.inc.h \ + core/tiny_alloc_fast_sfc.inc.h core/hakmem_tiny_fastcache.inc.h \ + core/box/front_metrics_box.h core/hakmem_tiny_lazy_init.inc.h \ + core/box/tiny_sizeclass_hist_box.h core/tiny_alloc_fast_inline.h \ + core/tiny_free_fast.inc.h core/hakmem_tiny_alloc.inc \ + core/hakmem_tiny_slow.inc core/hakmem_tiny_free.inc \ + core/box/free_publish_box.h core/mid_tcache.h \ core/tiny_free_magazine.inc.h core/tiny_superslab_alloc.inc.h \ core/box/superslab_expansion_box.h core/box/../tiny_tls.h \ core/tiny_superslab_free.inc.h core/box/free_remote_box.h \ core/box/free_local_box.h core/hakmem_tiny_lifecycle.inc \ - core/hakmem_tiny_slab_mgmt.inc core/tiny_fc_api.h + core/hakmem_tiny_slab_mgmt.inc core/hakmem_tiny_ace_guard_box.inc \ + core/tiny_fc_api.h core/hakmem_tiny.h: core/hakmem_build_flags.h: core/hakmem_trace.h: @@ -109,6 +116,12 @@ core/hakmem_tiny_bg_spill.h: core/tiny_adaptive_sizing.h: core/tiny_system.h: core/hakmem_prof.h: +core/hakmem_tiny_config_box.inc: +core/hakmem_tiny_ss_active_box.inc: +core/hakmem_tiny_globals_box.inc: +core/hakmem_tiny_publish_box.inc: +core/hakmem_tiny_legacy_slow_box.inc: +core/hakmem_tiny_tls_state_box.inc: core/front/quick_slot.h: core/front/../hakmem_tiny.h: core/front/fast_cache.h: @@ -138,14 +151,19 @@ core/tiny_box_geometry.h: core/tiny_region_id.h: core/hakmem_tiny_ultra_front.inc.h: core/hakmem_tiny_intel.inc: +core/hakmem_tiny_eventq_box.inc: core/hakmem_tiny_background.inc: core/hakmem_tiny_bg_bin.inc.h: core/hakmem_tiny_tls_ops.h: +core/hakmem_tiny_sll_cap_box.inc: +core/hakmem_tiny_ultra_batch_box.inc: core/hakmem_tiny_remote.inc: +core/hakmem_tiny_slab_lookup_box.inc: core/hakmem_tiny_init.inc: core/box/prewarm_box.h: core/hakmem_tiny_bump.inc.h: core/hakmem_tiny_smallmag.inc.h: +core/hakmem_tiny_phase6_wrappers_box.inc: core/tiny_atomic.h: core/tiny_alloc_fast.inc.h: core/tiny_alloc_fast_sfc.inc.h: @@ -169,4 +187,5 @@ core/box/free_remote_box.h: core/box/free_local_box.h: core/hakmem_tiny_lifecycle.inc: core/hakmem_tiny_slab_mgmt.inc: +core/hakmem_tiny_ace_guard_box.inc: core/tiny_fc_api.h: diff --git a/core/hakmem_tiny_lifecycle.d b/core/hakmem_tiny_lifecycle.d new file mode 100644 index 00000000..4576361c --- /dev/null +++ b/core/hakmem_tiny_lifecycle.d @@ -0,0 +1,38 @@ +core/hakmem_tiny_lifecycle.o: core/hakmem_tiny_lifecycle.c \ + core/hakmem_tiny_lifecycle.h core/hakmem_tiny.h \ + core/hakmem_build_flags.h core/hakmem_trace.h \ + core/hakmem_tiny_mini_mag.h core/hakmem_tiny_superslab.h \ + core/superslab/superslab_types.h core/hakmem_tiny_superslab_constants.h \ + core/superslab/superslab_inline.h core/superslab/superslab_types.h \ + core/tiny_debug_ring.h core/tiny_remote.h \ + core/hakmem_tiny_superslab_constants.h core/hakmem_tiny_tls_ops.h \ + core/hakmem_super_registry.h core/box/tiny_next_ptr_box.h \ + core/hakmem_tiny_config.h core/tiny_nextptr.h \ + core/hakmem_tiny_magazine.h core/hakmem_tiny_fastcache.inc.h \ + core/tiny_tls.h core/tiny_tls_guard.h core/hakmem_tiny_tls_list.h \ + core/box/ss_slab_meta_box.h core/box/../superslab/superslab_types.h +core/hakmem_tiny_lifecycle.h: +core/hakmem_tiny.h: +core/hakmem_build_flags.h: +core/hakmem_trace.h: +core/hakmem_tiny_mini_mag.h: +core/hakmem_tiny_superslab.h: +core/superslab/superslab_types.h: +core/hakmem_tiny_superslab_constants.h: +core/superslab/superslab_inline.h: +core/superslab/superslab_types.h: +core/tiny_debug_ring.h: +core/tiny_remote.h: +core/hakmem_tiny_superslab_constants.h: +core/hakmem_tiny_tls_ops.h: +core/hakmem_super_registry.h: +core/box/tiny_next_ptr_box.h: +core/hakmem_tiny_config.h: +core/tiny_nextptr.h: +core/hakmem_tiny_magazine.h: +core/hakmem_tiny_fastcache.inc.h: +core/tiny_tls.h: +core/tiny_tls_guard.h: +core/hakmem_tiny_tls_list.h: +core/box/ss_slab_meta_box.h: +core/box/../superslab/superslab_types.h: diff --git a/core/superslab/superslab_types.h b/core/superslab/superslab_types.h index 67d46f42..61338b42 100644 --- a/core/superslab/superslab_types.h +++ b/core/superslab/superslab_types.h @@ -60,6 +60,9 @@ typedef struct SuperSlab { uint32_t slab_bitmap; // active slabs (bit i = 1 → slab i in use) uint32_t nonempty_mask; // non-empty slabs (for partial tracking) uint32_t freelist_mask; // slabs with non-empty freelist (for fast scan) + // Phase 12-1.1: EMPTY slab detection for immediate reuse + uint32_t empty_mask; // slabs with used==0 (highest reuse priority) + uint8_t empty_count; // number of EMPTY slabs for quick check uint8_t active_slabs; // count of active slabs uint8_t publish_hint; uint16_t partial_epoch; diff --git a/hakmem_shared_pool.d b/hakmem_shared_pool.d index 5aec1a7d..dd6b7bfa 100644 --- a/hakmem_shared_pool.d +++ b/hakmem_shared_pool.d @@ -4,7 +4,7 @@ hakmem_shared_pool.o: core/hakmem_shared_pool.c core/hakmem_shared_pool.h \ core/superslab/superslab_types.h core/tiny_debug_ring.h \ core/hakmem_build_flags.h core/tiny_remote.h \ core/hakmem_tiny_superslab_constants.h core/box/ss_slab_meta_box.h \ - core/box/../superslab/superslab_types.h \ + core/box/../superslab/superslab_types.h core/box/ss_hot_cold_box.h \ core/box/pagefault_telemetry_box.h core/box/tls_sll_drain_box.h \ core/box/tls_sll_box.h core/box/../hakmem_tiny_config.h \ core/box/../hakmem_build_flags.h core/box/../tiny_remote.h \ @@ -32,6 +32,7 @@ core/tiny_remote.h: core/hakmem_tiny_superslab_constants.h: core/box/ss_slab_meta_box.h: core/box/../superslab/superslab_types.h: +core/box/ss_hot_cold_box.h: core/box/pagefault_telemetry_box.h: core/box/tls_sll_drain_box.h: core/box/tls_sll_box.h: diff --git a/hakmem_tiny_superslab.d b/hakmem_tiny_superslab.d index 0f079a01..3eff4c0a 100644 --- a/hakmem_tiny_superslab.d +++ b/hakmem_tiny_superslab.d @@ -3,7 +3,8 @@ hakmem_tiny_superslab.o: core/hakmem_tiny_superslab.c \ core/hakmem_tiny_superslab_constants.h core/superslab/superslab_inline.h \ core/superslab/superslab_types.h core/tiny_debug_ring.h \ core/hakmem_build_flags.h core/tiny_remote.h \ - core/hakmem_tiny_superslab_constants.h core/hakmem_super_registry.h \ + core/hakmem_tiny_superslab_constants.h core/box/ss_hot_cold_box.h \ + core/box/../superslab/superslab_types.h core/hakmem_super_registry.h \ core/hakmem_tiny.h core/hakmem_trace.h core/hakmem_tiny_mini_mag.h \ core/hakmem_tiny_config.h core/hakmem_shared_pool.h \ core/hakmem_internal.h core/hakmem.h core/hakmem_config.h \ @@ -20,6 +21,8 @@ core/tiny_debug_ring.h: core/hakmem_build_flags.h: core/tiny_remote.h: core/hakmem_tiny_superslab_constants.h: +core/box/ss_hot_cold_box.h: +core/box/../superslab/superslab_types.h: core/hakmem_super_registry.h: core/hakmem_tiny.h: core/hakmem_trace.h: