Files
hakmem/docs/analysis/C7_HOTBOX_DESIGN.md
Moe Charm (CI) 8f18963ad5 Phase 36-37: TinyHotHeap v2 HotBox redesign and C7 current_page policy fixes
- Redefine TinyHotHeap v2 as per-thread Hot Box with clear boundaries
- Add comprehensive OS statistics tracking for SS allocations
- Implement route-based free handling for TinyHeap v2
- Add C6/C7 debugging and statistics improvements
- Update documentation with implementation guidelines and analysis
- Add new box headers for stats, routing, and front-end management
2025-12-08 21:30:21 +09:00

190 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

C7 HotBox Design Memo
=====================
目的C7-only、mimalloc 風 TinyHeap、1 本の直線パス)
-------------------------------------------------------
- C7 (≈1KiB) 専用の最短経路を用意し、Gate から 1 本の直線で「取れるなら即返す」を実現する。
- mimalloc 風の「1 ページ1 個の free list」を C7HotBox 内に閉じ込め、Superslab/Warm との境界は slow 1 箇所に集約する。
- Box 化により、HAKMEM_TINY_C7_HOT=0 で完全に元の経路へ戻せるFail-Fast + Revertable
新しく導入する struct / 関数名の候補
------------------------------------
- `tiny_c7_page_t` : C7 ページのローカルメタ
- `void* free_list;` // ページ内 free list 先頭BASE
- `uint16_t used;` // 現在使用数
- `uint16_t capacity;` // ページ内の最大ブロック数
- `TinySlabMeta* meta;` / `SuperSlab* ss;` // 下層判定に必要なら保持
- `tiny_c7_heap_t` : スレッド専用の C7 ヒープ箱
- `tiny_c7_page_t* current_page;`
- `tiny_c7_page_t* partial_pages;` // まだ空きがあるページのリング/リスト
- `tiny_c7_page_t* full_pages;` // full になったページ
- API全て static inline で HotBox に閉じ込める)
- `tiny_c7_heap_t* tiny_c7_heap_for_thread(void);` // TLS からヒープを取得
- `void* tiny_c7_alloc_fast(size_t size);` // 1024 確定サイズ前提のホットパス
- `void* tiny_c7_alloc_slow_from_heap(tiny_c7_heap_t*);` // Superslab/Warm 境界はここ 1 箇所
- `void tiny_c7_free_fast(void* p);` // C7 free ホットパス
- `tiny_c7_page_t* tiny_c7_page_of(void* p);` // ポインタ→ページの helperクラス判定は既存メタを利用
- `void tiny_c7_page_becomes_empty(tiny_c7_page_t*);` // 全 free 判定後にのみ下層へ返す
フロー図(境界は 1 箇所)
------------------------
- alloc: `Gate → (size==1024 && HOT=1) → C7HotBox → (足りなければ) Superslab/Warm 経路`
- free : `Gate → C7HotBoxpage 内で完結)→ (全 free なら) Superslab/Tier/Guard に返す`
A/B 切替ポリシーHAKMEM_TINY_C7_HOT
--------------------------------------
- `HAKMEM_TINY_C7_HOT=1` : C7HotBox を有効化。Gate で `class_idx==7` を検出したときだけ `tiny_c7_alloc_fast` / `tiny_c7_free_fast` を経由する。
- `HAKMEM_TINY_C7_HOT=0` : 完全に従来経路へフォールバックUnified Cache / Warm / Superslab の既存ルート)。
- ENV で即時戻せるようにし、Box 境界は slow helper`tiny_c7_alloc_slow_from_heap` / `tiny_c7_page_becomes_empty`1 箇所に集約する。
- TinyHeap プロファイル: `HAKMEM_TINY_HEAP_PROFILE` を導入LEGACY/C7_SAFE/C7_ULTRA_BENCH/CUSTOM
- C7_SAFE: class mask=0x80, C7 meta_mode=1SAFE`HAKMEM_TINY_HEAP_BOX` 自動 ON。C7_HOT は別途 1 を推奨。
- C7_ULTRA_BENCH: class mask=0x80, C7 meta_mode=2bench 専用)。
- CUSTOM: 既存の `HAKMEM_TINY_HEAP_CLASSES` / `HAKMEM_TINY_C7_META_MODE` を直接指定。
- 推奨セット:
- 本番寄せ C7: `HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_LARSON_FIX=1`
- C7-only bench: `HAKMEM_TINY_HEAP_PROFILE=C7_ULTRA_BENCH HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_LARSON_FIX=1`
- 補足: class6 にも対称の Hot front`HAKMEM_TINY_C6_HOT` + class maskを追加済みだが、C6 は回帰が大きく bench/実験専用のまま。
- Phase16: Route Snapshot Box (`tiny_route_env_box.h`) を追加し、Gate は「size→class→route LUT→heap/legacy」の 1 分岐構造に整理。C7 SAFE profile でも mixed での回帰を縮めつつ、C7-only は LEGACY≈39.7M / SAFE≈41.1M / ULTRA≈46.1M (20k/ws=64, Release, LARSON_FIX=1)。
現状の位置づけ (Phase 19)
--------------------------
- C7 SAFE: C7-only 20k/ws64 ≈46.6M ops/s、Mixed 161024B は LEGACY と ±1M 以内。C7-heavy で推奨。
- C7 ULTRA_BENCH: C7-only 20k/ws64 ≈52Mbench 専用、Superslab/Tier 統計は緩むため本番では OFF
- C6 TinyHeap: C6-heavy / Mixed で throughput が明確に悪化(例: LEGACY≈44.3M → C6 TinyHeap≈38.6M)。`HAKMEM_TINY_HEAP_CLASSES=0x40/0xC0` は bench/実験専用マスクとして扱う。
メモ
----
- Remote Queue / Ownership / Publish/Adopt は触らず、C7HotBox は「C7 専用 TinyHeap」だけを責務とする。
- 可視化はワンショットまたは軽いカウンタのみ(常時ログは禁止)。
Phase 1.1: lookup 削減メモ
--------------------------
- free ホットパスから二重 lookup を除去:
- `tiny_c7_free_fast_with_meta(ss, slab_idx, base)` を追加し、Gate が持っている Superslab/スラブ index をそのまま渡す経路を用意。
- Larson fix (`HAKMEM_TINY_LARSON_FIX!=0`) で owner==self を確認できた場合のみ、この meta 渡し free を使う。cross-thread は従来通り remote queue へ。
- fallback 用の `tiny_c7_free_fast()` は安全側として残し、lookup が必要な場合だけ slow 経路へ倒す。
- `tiny_c7_page_of()` を TLS fast-first 化:
- TLS C7 slab の範囲内であれば `hak_super_lookup`/`slab_index_for` を呼ばずに即 attach。
- 範囲外のみ従来の Superslab lookup にフォールバック。
Phase 2: 可視化と Tiny lane 整合
---------------------------------
- `HAKMEM_TINY_C7_HEAP_STATS` を追加し、C7 TinyHeap 内のステップ別カウンタalloc_fast_current / alloc_slow_prepare / free_fast_local / free_slow_fallback / alloc_prepare_fail / alloc_failを計測できるようにした。`HAKMEM_TINY_C7_HEAP_STATS_DUMP=1` で終了時にダンプ。
- meta 軽量化の足場として `HAKMEM_TINY_C7_META_LIGHT` を追加Phase 3 でベンチ用実装を追加)。
- Gate (`hak_alloc_at`) で size==1024 かつ TinyHeap front ON の場合は Tiny lane 失敗扱いにせず、最後まで TinyHeapBox 経路として扱うようにして警告を抑止。
- 参考値C7-only 20k, stats ON: alloc_fast_current=10052 / alloc_slow_prepare=7681 / free_fast_local=10053 / free_slow_fallback=0 / alloc_prepare_fail=0 / alloc_fail=0。
Phase 3: stride キャッシュ + meta-lightbench
------------------------------------------------
- `tiny_heap_class_t` に stride キャッシュを持たせ、ctx 初期化時に全クラスの stride を前計算。alloc/pop は hcls->stride を直接参照。
- free 側で class7 は「free した page を current_page に優先」するように変更し、alloc_slow_prepare を減らす方向へ調整。
- `HAKMEM_TINY_C7_META_LIGHT=1` で meta->used / ss_active_* の per-alloc 更新をスキップする実験モードを実装(デフォルト OFF、Superslab/Tier stats は本番向けでは緩むため bench 専用)。
- ベンチC7-only 20k/ws=64, Release):
- legacy (HEAP_BOX=0 HOT=1): ≈42.5M ops/s
- TinyHeap front (HEAP_BOX=1 HOT=1 LARSON_FIX=1, META_LIGHT=0): ≈43.2M ops/s、stats=alloc_fast_current=10052 / alloc_slow_prepare=7681 / free_fast_local=10053
- TinyHeap front + meta-light (META_LIGHT=1): ≈48.1M ops/s、stats=alloc_fast_current=5837 / alloc_slow_prepare=5179 / free_fast_local=8727
Phase 4: meta-light をページ境界バッチ flush 化bench 用)
------------------------------------------------
- `tiny_heap_page_t` に C7 delta (`c7_used_delta` / `c7_active_delta`) を持たせ、meta-light ON では per-alloc で meta/active を触らず delta のみ更新。
- ページが empty になる/ノード解放時に `tiny_c7_meta_flush_page()` で delta をまとめて meta->used / total_active_blocks に反映(負 delta は `ss_active_dec_one` ループで処理する素朴版)。
- 依然として bench/研究用フラグでデフォルト OFF本番 stats は OFF 時の挙動を維持)。
- ベンチC7-only 20k/ws=64, Release):
- META_LIGHT=0: ≈41.9M ops/salloc_fast_current=10052 / alloc_slow_prepare=7681 / free_fast_local=10053
- META_LIGHT=1バッチ flush: ≈53.5M ops/salloc_fast_current=11013 / alloc_slow_prepare=3 / free_fast_local=9294
Phase 5: delta debug フックmeta-light 検証用)
---------------------------------------------
- `HAKMEM_TINY_C7_DELTA_DEBUG` を追加し、meta-light ON で `tiny_c7_heap_debug_dump_deltas()`core/box/tiny_heap_box.hから class7 ノードの delta を stderr に出力できるようにした。
- `core/hakmem_tiny.c` の destructor でも同 helper を呼び、`HAKMEM_TINY_C7_META_LIGHT=1 HAKMEM_TINY_C7_DELTA_DEBUG=1` のときベンチ終了時に自動ダンプ。
- 長時間 C7-only ベンチ例 (ws=64, Release):
- 100k: META_LIGHT=1+DELTA_DEBUG ≈51.3M ops/s、delta 残 = idx0 used_delta=7669 active_delta=7669 used=6。
- 200k: META_LIGHT=1+DELTA_DEBUG ≈48.1M ops/s、delta 残 = idx0 used_delta=14727 active_delta=14727 used=6。
- delta が live page に残り続けるempty/release でのみ flush する設計のため)ことが見えた。今後は閾値 flush や current/partial の入替で長時間ランでも delta を抑える改善を検討。
Phase 6: delta 閾値 flush + attach clampbench
------------------------------------------------
- `tiny_c7_delta_should_flush()` を追加し、C7 meta-light ON かつ `|delta| >= max(256, capacity*16)` のときホットパスから `tiny_c7_meta_flush_page()` を呼び出すようにした。per-alloc atomic を避けつつ delta を capacity 数倍にバウンド。
- `tiny_heap_attach_page()` で C7 meta-light 有効時は `used``capacity` へ clamp + c7_delta を 0 クリアし、過去ランの meta->used が膨らんでいても TLS ノードを安全に再利用できるようにした。
- ベンチ (C7-only 20k/ws=64, Release):
- Legacy HEAP_BOX=0 HOT=1: ≈42.5M ops/s
- TinyHeap HEAP_BOX=1 HOT=1 LARSON_FIX=1 META_LIGHT=0: ≈43.1M ops/s
- TinyHeap META_LIGHT=1 (閾値 flush/clamp): ≈42.6M ops/s
- 長時間 C7-only (ws=64, DELTA_DEBUG=1):
- 100k: `[C7_DELTA_SUMMARY] nonzero_pages=0 used_delta_sum=0 active_delta_sum=0`
- 200k: 同上 (delta 0) → delta が無制限に積もらないことを確認。
Phase 7: クラス選択式 TinyHeapC6/C5 拡張のためのゲート)
------------------------------------------------------
- ENV `HAKMEM_TINY_HEAP_CLASSES` を追加bitmask、デフォルト 0x80=C7 のみ)。`tiny_heap_class_route_enabled(cls)` で TinyHeap front を使うクラスを判定し、C6/C5 も段階的に TinyHeap へ載せ替え可能にした。
- Gate: `malloc_tiny_fast` / `free_tiny_fast` がクラスごとに TinyHeap 経路を選択。C7 は `tiny_c7_heap_mode_enabled()``HAKMEM_TINY_C7_HOT` 連動)を維持しつつ、他クラスは `tiny_heap_alloc/free_class_fast()` を使う薄ラッパで扱う。
- TLS SLL 側もクラス単位で分離し、`sll_refill_small_from_ss` / `sll_refill_batch_from_ss` / `hak_tiny_prewarm_tls_cache` が TinyHeap クラスを早期 return/skip。C7 は「TinyHeapBox ↔ Superslab/Tier/Guard」だけを踏む二層構造のまま。
Phase 8: C6 を TinyHeap に載せた観測
-------------------------------------
- TinyHeap ON + `HAKMEM_TINY_HEAP_CLASSES=0x40` で C6 を TinyHeap に載せ、SLL/prewarm/refill が early return していることを確認C7 と同じ二層構造で動作)。
- ベンチ (Release, iters=20k, ws=256, min=257 max=768): TinyHeap OFF ≈45.7M ops/s、C6 TinyHeap ≈39.7M ops/s、C6+C7 TinyHeap (mask=0xC0) ≈34.1M ops/s。
- Mixed 161024B (iters=20k, ws=256): TinyHeap OFF ≈46.8M ops/s、C7 only TinyHeap ≈39.4M ops/s、C6+C7 TinyHeap ≈33.8M ops/s。いずれも Tiny lane failed 警告が出るケースありGate 側の判定/フォールバック整理が今後の課題)。
Phase 9: Tiny lane 判定を TinyHeap と揃える
------------------------------------------
- Gate (`hak_alloc_at`) が TinyHeap 経路を「Tiny lane の成功」として扱うように修正。class_idx を保持し、TinyHeap クラスなら TinyHeap 直接呼び出しも試行し、NULL の場合は静かなフォールバック(警告は legacy Tiny route のみ)。
- 警告抑止後のベンチ (Release, iters=20k, ws=256):
- C6 偏重 (min=257 max=768): OFF≈47.8M / C6 only TinyHeap≈39.2M / C6+C7 TinyHeap≈31.3M(警告なし)。
- Mixed 161024B: OFF≈47.6M / C7 only TinyHeap≈36.9M / C6+C7 TinyHeap≈30.3M(警告なし)。
- 依然として性能は TinyHeap OFF より低いケースがあるため、C6/C7 の slow_prepare 削減や current_page 利用強化を次フェーズで行う。
Phase ULTRA: C7 meta モードの三段化bench 専用 ULTRA 追加)
------------------------------------------------------------
- ENV `HAKMEM_TINY_C7_META_MODE` を追加0:OFF, 1:SAFE meta-light=従来の delta+閾値 flush/clamp, 2:ULTRA。未指定時は `HAKMEM_TINY_C7_META_LIGHT` を後方互換ゲートとして扱い SAFE=1 相当。
- ULTRA(mode=2) は C7-only ベンチ専用。per-alloc で meta->used / ss_active_* を一切更新せず、delta/flush もスキップする高速モード。2024-xx-xx 時点で pop/push の meta->freelist/carved atomic も省略し、ベンチ専用の最小オーバーヘッド構成にしているBox 境界は維持しつつ Superslab/Tier の統計整合は犠牲にする)。
- SAFE(mode=1) はこれまでのページ境界 flush + 閾値 flush + attach clamp を維持し、本番で使うなら mode=0/1。ULTRA は「bench/研究用途のみ」と明記。
- C7-only 20k/ws=64 (Release, HEAP_BOX=1, HEAP_CLASSES=0x80, HOT=1, LARSON_FIX=1):
- mode=0: ≈38.7M ops/salloc_fast_current=10052 / alloc_slow_prepare=7681
- mode=1: ≈34.1M ops/salloc_fast_current=5837 / alloc_slow_prepare=5179
- mode=2: ≈41.6M ops/salloc_fast_current=5948 / alloc_slow_prepare=5068
- current_page 可視化Phase 11: `g_c7_page_stats` を追加し、prepare_calls / prepare_with_current_null / prepare_from_partial / current_set_from_free / current_dropped_to_partial を `HAKMEM_TINY_C7_HEAP_STATS=1` でダンプ。C7-only ULTRA (20k/ws=64) では `prepare_with_current_null=prepare_calls` となり、free 側で current_page を保持できていないことが見えたため、current 固定化ポリシーを継続検討中。
- current 固定化の初期版ULTRA 専用): empty/full での unlink を避け、free 後は常に current_page に据え置く + prepare で current を優先するように変更。C7-only 20k/ws=64 (mode=2) で ops≈52.0M、alloc_fast_current=11015 / alloc_slow_prepare=1 / prepare_calls=1 まで改善。SAFE への逆輸入は未定ULTRA は bench 限定)。
- SAFE (mode=1) への current ポリシー逆輸入 (Phase 12):
- free で used>0 の page を current に据え直し、empty でも delta flush のみで detach/publish を避けて current を保持。mark_full でも C7 meta_light が current を指す場合は unlink しない。
- prepare_page も C7 meta_light で current に空きがあれば即 returnrefill へ降りない)。
- ベンチ (C7-only, ws=64, HEAP_BOX=1, HEAP_CLASSES=0x80, HOT=1, LARSON_FIX=1): SAFE mode=1 20k ≈46.6M ops/salloc_fast_current=11015 / alloc_slow_prepare=1 / free_fast_local=8726, prepare_calls=1。長時間 100k≈46.7M / 200k≈44.99M、`HAKMEM_TINY_C7_DELTA_DEBUG=1` でも delta 残 0 を確認。
- ULTRA(mode=2) は bench 専用のまま。本番寄り構成は mode=0/1 を推奨。
Phase 13: mixed での SAFE 効果と multi-class stats
---------------------------------------------------
- Stats を `TinyHeapClassStats[]` に拡張ENV: `HAKMEM_TINY_HEAP_STATS` / `_DUMP`、旧 `_C7_` 互換し、C6/C7 の fast/slow/fallback を同時に計測可能にした。
- Mixed 161024B (iters=20k, ws=256, LARSON_FIX=1):
- TinyHeap OFF: ≈43.7M ops/s。
- C7 SAFE のみ TinyHeap (`HEAP_BOX=1 HEAP_CLASSES=0x80 META_MODE=1 HOT=1`): ≈44.9M ops/s`HEAP_STATS[7] fast=5691 slow_prepare=1`)。
- C6+C7 TinyHeap (`HEAP_CLASSES=0xC0`): ≈39.3M ops/s`HEAP_STATS[6] fast=2744 slow=1`, `HEAP_STATS[7] fast=5691 slow=1`)。
- C6 偏重 (min=257 max=768):
- TinyHeap OFF: ≈43.8M ops/s。
- C6 TinyHeap のみ: ≈38.5M ops/s`HEAP_STATS[6] fast=5372 slow=1`)。
- C6+C7 TinyHeap: ≈40.6M ops/s`HEAP_STATS[6] fast=5372 slow=1`, `HEAP_STATS[7] fast=5691 slow=1`)。
- 方針: C7 SAFE は mixed でも悪化せず、C7-only では legacy 超え → デフォルト TinyHeap 候補。C6 は slow_prepare 自体は少ないが経路オーバーヘッドで低下するため、当面は bench/実験用 (HEAP_CLASSES=0x40/0xC0)。C7-only を突き抜けるベンチは ULTRA (META_MODE=2) を手動で使う。
Phase 17: C7 フロント超直線化HotPipeline 前倒し)
----------------------------------------------------
- Route Snapshot を使った C7 判定ヘルパ `tiny_c7_front_uses_heap()` を追加。Gate から class7 が TinyHeap 経路かどうかを 1 LUT で判定できるようにした。
- `malloc_tiny_fast` の冒頭に「size==1024 かつ C7 route=HEAP」専用パスを追加。クラス判定/LUT/route を飛ばして `tiny_c7_alloc_fast` へ直行し、miss 時だけ Legacy Tiny slow (`tiny_cold_refill_and_alloc(7)`) に静かにフォールバック。
- `free_tiny_fast` も C7 route=HEAP を先に評価し、Larson fix ブロックの owner 判定後に `tiny_c7_free_fast_with_meta` へ直行する経路を明示route はスナップショットから 1 回だけ読む)。
- ベンチ (Release, iters=20k, ws=64, LARSON_FIX=1, HOT=1):
- C7-only: PROFILE=LEGACY ≈37.1M / C7_SAFE ≈38.2M / C7_ULTRA_BENCH ≈45.3M ops/s。
- Mixed 161024B (ws=256): PROFILE=LEGACY ≈40.3M / C7_SAFE ≈40.7M ops/s差 ~-1M まで縮小)。
- 今後の選択肢メモ: (A) C6 TinyHeap を C7 SAFE 流current 固定meta-light SAFEに寄せるか、(B) Tiny front/gate/UC の命令削減を perf で詰めるかを次フェーズで決める。
Phase 18 メモC6 SAFE 実験とプロファイル整理)
------------------------------------------------
- TinyHeap プロファイル箱に加え、`HAKMEM_TINY_C6_META_MODE`0/1を追加。現状の C6 SAFE は整合優先で挙動は mode 0 相当delta/flush 未使用、meta/active は per-alloc 更新)だが、クラスマスクで C6 を TinyHeap に載せたまま A/B 計測できる。
- C6 偏重 20k/ws=256 では LEGACY ≈44.3M → C6 TinyHeap (mask=0x40, META_MODE=0/1) ≈38.6〜38.8M、C6+C7 TinyHeap (0xC0, META_MODE=1) ≈39.9M。Mixed 161024B でも C6 TinyHeap は ≈38.5〜38.7M、C6+C7 TinyHeap ≈39.5Mslow_prepare はいずれも ≈1 と低い)。
- デフォルト profile は引き続き C7 SAFE / C7 ULTRA_BENCH / LEGACY の 3 択。C6 を TinyHeap に載せるのは bench/研究用(クラスマスク 0x40/0xC0 を明示)とし、本番では 0x80=C7 のみを推奨。C6 向け meta-light を安全に再導入する場合は Superslab 解放まわりの安全性を再確認する。
TinyHeapBox への載せ替えPhase 1.0 構造)
------------------------------------------
- C7HotBox の実体を `core/box/tiny_heap_box.h` の汎用 TinyHeapBox 上に配置し、型は `tiny_heap_ctx_t` / `tiny_heap_page_t` へ統一。
- ENV `HAKMEM_TINY_HEAP_BOX=1` かつ `HAKMEM_TINY_C7_HOT=1` のとき、Gate から class7 の alloc/free を TinyHeap front 経由に切り替える。
- TinyHeapBox は class ごとに current/partial/full と固定ノードを TLS に保持し、下層 Box (Warm/Superslab/Tier/Guard) との接続は `tiny_heap_alloc_slow_from_class()` / `tiny_heap_page_becomes_empty()` の 1 箇所に集約。
- C7 固有 API は薄いラッパのみ (`tiny_c7_alloc_fast` / `tiny_c7_free_fast_with_meta` など) とし、今後 C5〜C6 も同じ基盤に載せ替えられる構造にした。
- C7 + TinyHeap front では TLS SLL 経路を完全に無効化し、refill/prewarm/push/slow path すべてを TinyHeapBox↔Superslab/Tier/Guard の二層に固定した(`HAKMEM_TINY_SLL_LOG_ANY=1` でも C7 push ログ 0、20k ループ完走)。