C7 meta-light delta flush threshold and clamp

This commit is contained in:
Moe Charm (CI)
2025-12-07 22:42:02 +09:00
parent fda6cd2e67
commit 9c68073557
4 changed files with 949 additions and 4 deletions

View File

@ -0,0 +1,108 @@
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 箇所に集約する。
メモ
----
- 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 が無制限に積もらないことを確認。
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 ループ完走)。

View File

@ -0,0 +1,100 @@
TinyHeapBox Design (C7 先行載せ替え)
====================================
概要
----
- 目的: Unified Cache を経由せず、mimalloc 風の TinyHeap (page 内 freelist + current/partial/full) を 1 箱に閉じ込める。
- Box 境界: Superslab / Warm / Tier / Guard との接続は
- alloc 側: `tiny_heap_alloc_slow_from_class()` (ページ枯渇時だけ)
- free 側: `tiny_heap_page_becomes_empty()` (全 free になった瞬間だけ)
の 1 箇所に集約し、ホットパスは TinyHeap 内で完結させる。
- A/B: `HAKMEM_TINY_HEAP_BOX=0` → 従来 Unified front、`=1` → TinyHeap front (いまは C7 のみ)。
主要構造 (core/box/tiny_heap_box.h)
-----------------------------------
- `tiny_heap_page_t`: ページ内 freelist/used/capacity と Superslab メタ (ss/meta/slab_idx/base) を保持。
- `tiny_heap_class_t`: class ごとの current_page / partial_pages / full_pages と固定ノード配列 (`TINY_HEAP_MAX_PAGES_PER_CLASS` デフォルト4) に加え、stride をキャッシュ。
- `tiny_heap_ctx_t`: TLS の TinyHeap コンテキスト。全クラス分の `tiny_heap_class_t` を持ち、初回だけ memset で初期化。
- API (static inline):
- `tiny_heap_ctx_for_thread()` / `tiny_heap_class()` アクセサ
- `tiny_heap_attach_page(ctx, cls, ss, slab_idx)` / `tiny_heap_page_of(ctx, cls, base)` (TLS fast-first → fallback lookup)
- `tiny_heap_alloc_class_fast()` / `tiny_heap_alloc_slow_from_class()` ホット/スロー境界
- `tiny_heap_free_class_fast_with_meta()` / `tiny_heap_free_class_fast()` free ホットパス
- ポリシー:
- freelist pop で meta->used / ss_active_add をインクリメント。carve は `meta->carved++` を併走させ、Unified/旧経路との整合を維持。
- free で meta->used / ss_active_dec_one をデクリメント。empty になったら `ss_partial_publish()` のヒントだけ出してノードを解放。
C7HotBox への適用 (core/box/tiny_c7_hotbox.h)
---------------------------------------------
- `tiny_c7_*` 型は TinyHeapBox の型 alias。C7 固有 API は薄いラッパのみ:
- `tiny_c7_alloc_fast(size)``tiny_heap_alloc_class_fast(ctx, 7, size)`
- `tiny_c7_free_fast_with_meta(ss, slab_idx, base)``tiny_heap_free_class_fast_with_meta(ctx, 7, ...)`
- `tiny_c7_page_of(base)``tiny_heap_page_of(ctx, 7, base)`
- ENV: `HAKMEM_TINY_HEAP_BOX=1` かつ `HAKMEM_TINY_C7_HOT=1` のときだけ Gate で class7 を TinyHeap 経路に切り替え。
- 既存の C7 専用 TLS 状態 (`g_tiny_c7_hot_heap`) は廃止し、共通 `g_tiny_heap_ctx` に統合。
Front Gate (core/front/malloc_tiny_fast.h)
------------------------------------------
- alloc: class_idx==7 & size==1024 かつ TinyHeapBox ON で `tiny_c7_alloc_fast()` に直行。それ以外は Unified Cache front。
- free : Larson owner 判定に関係なく、TinyHeapBox ON + class7 なら meta 渡し free (`tiny_c7_free_fast_with_meta`) を優先。ss_fast_lookup 失敗時は `tiny_c7_free_fast()` で安全側。
C7 は TLS SLL を使わないTinyHeap front ON 時)
-----------------------------------------------
- ENV 結合: `tiny_c7_heap_mode_enabled()` = `HAKMEM_TINY_HEAP_BOX=1` かつ `HAKMEM_TINY_C7_HOT=1`
- Refill/prewarm: `sll_refill_small_from_ss()` / `sll_refill_batch_from_ss()` は C7 を即 return。`hak_tiny_prewarm_tls_cache()` も C7 prewarm を skip。
- Push ガード: `tls_sll_push_impl()` で class7+TinyHeap front を拒否False return
- Slow path バイパス: `hak_tiny_alloc_slow()` で同条件なら TinyHeapBox (`tiny_c7_alloc_fast`) に委譲し、旧 slow 経路を通さない。
- ベンチ: `HAKMEM_BENCH_C7_ONLY=1` 20k ループ / `HAKMEM_TINY_SLL_LOG_ANY=1` でも C7 の TLS SLL ログは 0、`HAKMEM_TINY_HEAP_BOX=1` で SEGV なく完走 (≈4246M ops/s)。レガシー (`HEAP_BOX=0`) も ≈41.8M ops/s で回帰なし。
Phase 2: 可視化と警告抑止
-------------------------
- `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` で終了時に stderr ダンプ。
- meta-light フラグ `HAKMEM_TINY_C7_META_LIGHT` を追加Phase 3 でベンチ用実装を追加。ON のときは meta->used / ss_active_* の per-alloc 更新をスキップし、page->used のみで管理するベンチ専用モード。
- Tiny lane failed 警告は TinyHeap front ON の C7 では出さないように `hak_alloc_at` で C7 を 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
-----------------------------------------------
- ctx 初期化時に全クラスの stride を前計算し、`tiny_heap_page_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 で delta のみ更新。
- `tiny_c7_meta_flush_page()` を導入し、ページが空になる/ノード解放時に delta をまとめて meta->used / total_active_blocks に反映(負 delta は `ss_active_dec_one` ループで処理する素朴版)。
- Box 境界は従来通り `tiny_heap_alloc_slow_from_class()``tiny_heap_page_becomes_empty()` に集約。meta-light は bench/研究用でデフォルト 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 フック + 長時間ラン確認
-------------------------------------------
- `HAKMEM_TINY_C7_DELTA_DEBUG` を追加し、meta-light ON 時に `tiny_c7_heap_debug_dump_deltas()` で class7 ノードの `c7_used_delta` / `c7_active_delta` をダンプ可能にした。per-TU static だが ENV ゲート付き。
- `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 や partial→current の調整で長時間ランでも 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 し、過去ラン由来の meta->used が異常に大きくても current/partial を復帰できるようにした(同時に c7_*_delta を 0 にリセット)。
- 長時間 C7-only (ws=64, Release, DELTA_DEBUG=1):
- 100k: delta summary = nonzero_pages=0 used_delta_sum=0 active_delta_sum=0
- 200k: 同上delta=0
- ベンチ (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、delta debug なしでも off と同等レンジに戻った。
今後の拡張ステップ
------------------
- C5〜C6 を TinyHeapBox に移す際は `tiny_heap_alloc_class_fast()` を流用し、Box 境界 (ページ補給/返却) の 1 箇所化を維持する。
- `TINY_HEAP_MAX_PAGES_PER_CLASS` の調整と stats/Tier 更新の扱いは次フェーズで検証。
- TLS SLL は今後 C0〜C6 のための箱として維持し、C7 は TinyHeapBox↔Superslab/Tier/Guard の二層構造に限定する。