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 なく完走 (≈42–46M 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-light(bench) ----------------------------------------------- - 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/s(alloc_fast_current=10052 / alloc_slow_prepare=7681 / free_fast_local=10053) - META_LIGHT=1(バッチ flush): ≈53.5M ops/s(alloc_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 時 clamp(bench) --------------------------------------------------- - `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 と同等レンジに戻った。 Phase 7: クラス選択式 TinyHeap(C6 拡張の足場) ------------------------------------------------ - ENV `HAKMEM_TINY_HEAP_CLASSES` を追加(bitmask, デフォルト 0x80=C7 のみ)。`tiny_heap_class_route_enabled(cls)` で TinyHeap front を使うクラスを判定し、C6/C5 を段階的に載せ替える A/B を可能にした。 - Front gate: `malloc_tiny_fast` / `free_tiny_fast` がクラスごとに TinyHeap 経路を選択。C7 は従来通り `tiny_c7_heap_mode_enabled()`(`HAKMEM_TINY_C7_HOT` 連動)でガードし、C6 などは `tiny_heap_alloc/free_class_fast()` に直行。 - TLS SLL との切り離しをクラス単位に拡張: `sll_refill_small_from_ss` / `sll_refill_batch_from_ss` / `hak_tiny_prewarm_tls_cache` は `tiny_heap_class_route_enabled(cls)` なら即 return/skip。TinyHeap クラスは Superslab↔TinyHeapBox のみを通る。 - 例: `HAKMEM_TINY_HEAP_CLASSES=0x40` で C6 だけ TinyHeap、`0xC0` で C6+C7 TinyHeap。今後のベンチで C6-only / mixed ワークロードの hit 率と slow_prepare 割合を確認する。 今後の拡張ステップ ------------------ - 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 の二層構造に限定する。