Files
hakmem/docs/analysis/TINY_HEAP_BOX_DESIGN.md
2025-12-07 22:49:28 +09:00

108 lines
10 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.

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 と同等レンジに戻った。
Phase 7: クラス選択式 TinyHeapC6 拡張の足場)
------------------------------------------------
- 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 の二層構造に限定する。