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

20 KiB
Raw Blame History

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 helpertiny_c7_alloc_slow_from_heap / tiny_c7_page_becomes_empty1 箇所に集約する。
  • TinyHeap プロファイル: HAKMEM_TINY_HEAP_PROFILE を導入LEGACY/C7_SAFE/C7_ULTRA_BENCH/CUSTOM
    • C7_SAFE: class mask=0x80, C7 meta_mode=1SAFEHAKMEM_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 frontHAKMEM_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 有効時は usedcapacity へ 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/sHEAP_STATS[7] fast=5691 slow_prepare=1)。
    • C6+C7 TinyHeap (HEAP_CLASSES=0xC0): ≈39.3M ops/sHEAP_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/sHEAP_STATS[6] fast=5372 slow=1)。
    • C6+C7 TinyHeap: ≈40.6M ops/sHEAP_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_MODE0/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 ループ完走)。