Files
hakmem/docs/analysis/TINY_HEAP_V2_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

336 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.

TinyHeap v2 Design (入口メモ)
============================
現状の v2 (Phase32 時点)
------------------------
- C7 専用で「1 枚 lease + current/freelist を v2 で握る」状態。ページ供給・meta/ss_active/Remote/Stats はすべて v1 TinyHeap/C7 SAFE に委譲。
- A/B でいつでも v1 に戻せる(`HAKMEM_TINY_HOTHEAP_V2` / `HAKMEM_TINY_HOTHEAP_CLASSES`)。性能はまだ v1 と同等を維持するのが目的。
Phase33: C7 v2 ON/OFF A/B現状評価
-----------------------------------
- 条件: C7 SAFE (`PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_LARSON_FIX=1`), class mask 0x80, HEAP_STATS=ON。
- C7-only (ws=64, iters=20k): v2 OFF **39.42M ops/s** / v2 ON **43.55M ops/s**cls7 fast=11015 / slow=1、v2 alloc/free カウンタ増加)。
- Mixed 161024B (ws=256, iters=20k): v2 OFF **40.44M ops/s** / v2 ON **36.58M ops/s**cls7 fast=5691 / slow=1、v2 カウンタ増加)。
- 現状の v2 は current/freelist を自前化しつつ、pop/free は v1 の `tiny_heap_page_pop/free_local` を呼ぶラッパ。C7-only はわずかにプラスだが、Mixed では lease 判定や v1 呼び出し重複のオーバーヘッドが目立つ。Phase34 で枝/ロード削減を検討。
ゴール
-----
- mimalloc の heap→page→block に近い形で C5/C6/C7 を 1 つの TinyHotHeap に統合し、Gate/UC/TLS-SLL をさらに薄くする。
- v1 で得た C7 SAFE の current 固定・delta/Stats Box をクラス共通のポリシーとして一般化し、Tiny 層全体の命令数を半分近く狙う。
- 学習層はこれまで通り「外の箱」Policy Snapshotに閉じ込め、ホットパスはポリシー値を読むだけにする。
前提
----
- Cold Stats/Guard/Tier は v1 の Cold Stats Box を土台にし、バッチ/集計を Cold 側で吸収する。
- C6 TinyHeap は v1 では凍結したまま。v2 では C5C7 を最初から設計し直す前提で扱う。
方針候補(箇条書き)
-------------------
- C5C7 を 1 つの TinyHotHeap にまとめ、class ごとの current_page ポリシーC7 SAFE を一般化)を持たせる。
- Gate/Route を「size→class→hotheap/legacy」の 1 LUT + 1 分岐に統一し、C6/C7 の直線フロントをデフォルト化。
- Stats Box を前提に、meta->used / ss_active_* は Hot 側で直接触らず、イベント + バッチ更新に寄せる。
- C6 は v1 で封印したまま、v2 で C7 SAFE 流の current/delta を最初から組み込む。
- 学習/Policy は Snapshot Box 化し、ホットパスはポリシー値読み取りのみ(学習の ON/OFF でホットパスの命令数が変わらないようにする)。
ゴール / 非ゴールv2 スコープの固定)
-------------------------------------
- ゴール:
- C5C7 を 1 つの TinyHotHeapHotHeap v2に統合し、heap→page→block のみに集中するホット層を構築する。
- C7-only / Mixed で mimalloc に 1.5〜2× 近づける “理論上” の構造(フロントと Cold 更新の命令数を半減させる)。
- Hot 層は current 固定delta/Stats Box バッチを前提とし、学習層は Snapshot Box に閉じ込めてホットパスから排除する。
- 非ゴール:
- v2 では C0C4 や巨大サイズ帯は触らないTiny 以外は対象外)。
- 学習層のポリシー/ブの仕様変更は行わないSnapshot の読み取りだけを継続)。
- v1 の C7 SAFE プロファイルを壊さないv1/v2 の A/B 共存を前提)。
箱構造HotHeap v2 の 1 枚図イメージ)
--------------------------------------
- TinyHotHeapBox (per-thread):
- hot.cls[5..7] に current_page / partial / full / stride を持つ。
- API:
- hot_alloc(ci) / hot_free(ci, p)
- hot_refill(ci)cold へ 1 箇所だけ触れる境界)
- ColdSuperslabBox / ColdStatsBox / PolicySnapshotBox / LearningBox外側の箱:
- Hot からは「イベント/要求」を 1 回だけ投げる。
- ColdStatsBox は delta→バッチ→meta/ss_active_* 反映を担うv1 Stats Box 互換インタフェースを維持)。
移行戦略と Gate/ENV
------------------
- フラグ:
- HAKMEM_TINY_HOTHEAP_V2=1 で v2 を有効化(デフォルト OFF
- HAKMEM_TINY_HOTHEAP_CLASSES でクラスマスク(初期は 0x80=C7 のみ、段階的に 0xE0=C5C7 へ)。
- A/B 方針:
- v1 TinyHeapC7 SAFEと v2 HotHeap を完全に切り替え可能にする。
- Gate/Route は snapshot LUT で「class→route(v1/v2/legacy)」を決定し、1 LUT + 1 分岐の形を維持。
v2 で“変えない”もの
-------------------
- Learning/ACE/ELO: PolicySnapshot の更新だけを学習側が持ち、ホットパスは snapshot 値を読むだけ。
- Cold Stats Box 呼び出しインタフェース: v1 と互換flush イベントの場所は同じ)。
- Fail-Fast 方針: 範囲外・magic 不一致は即 abort。Superslab/Tier/Guard の不変条件を崩さない。
実装ステップ3 分割プラン)
---------------------------
1. Step1: v2 用 TinyHotHeapBox 型と APIhot_alloc/free/refillを追加し、コンパイルパスだけ通すまだ未使用
2. Step2: C7-only を v2 に載せる A/BHOTHEAP_V2=1 & mask=0x80 で C7 だけ新経路、v1 と並走)。
3. Step3: Mixed のクラス分布を見ながら C6→C5 の順で HotHeap へ移すか判断(マスク 0xC0→0xE0 を段階的に開ける)。
Phase30 (done): 骨組みだけコードに追加
---------------------------------------
- core/box/tiny_hotheap_v2_box.h に v2 用の page/class/ctx 型と stub APIalloc/free/tlsを追加。
- ENV gate だけ先行HAKMEM_TINY_HOTHEAP_V2、HAKMEM_TINY_HOTHEAP_CLASSESを用意し、既存経路とは未接続。
- TLS スロットも確保したが、alloc/free は現時点では NULL/no-op。フロント配線は Phase31 以降で A/B 導入予定。
Phase31: C7-only を v2 ラッパ経由で A/B 可能に
----------------------------------------------
- Gate: `tiny_c7_hotheap_v2_enabled()``HAKMEM_TINY_HOTHEAP_V2=1` かつ mask bit7を追加。
- Route snapshot: bit7 が立っていれば `g_tiny_route_class[7]=TINY_ROUTE_HOTHEAP_V2` に設定(デフォルトは Legacy/HEAP のまま)。
- Front: `malloc_tiny_fast` / `free_tiny_fast` の C7 直線パスで v2→v1→legacy slow の順に試行v2 が NULL のとき v1 にフォールバック)。
- Impl: v2 alloc/free は現時点で v1 の薄ラッパ実際の挙動は変えない。TLS ctx で C7 stride を初期化し、簡易カウンタで v2 パスのヒットを把握できるようにした。他クラスは v2 未対応のまま。Superslab/Remote/Stats など Cold 処理はすべて v1 に委譲。
- A/BRelease, HEAP_STATS=ON: C7-only 43.28Mv2 ON/OFF 差なし、Mixed 161024B は LEGACY 42.18M / C7_SAFE v2 OFF 41.15M / v2 ON 40.74Mcls7 fast=5691 / slow=1 で一致、v2 カウンタ増加のみ)。
Phase32: C7 で current_page+freelist を v2 側に持ちつつ、ページ供給は v1 から lease
----------------------------------------------------------------------------
- v1 に `tiny_heap_c7_lease_page_for_v2()` を置き、C7 SAFE が保持しているページ情報meta/ss/base/capacityを lease する薄い境界を追加。
- v2 TLS ctx に C7 用の storage_page を持たせ、current_page が空のとき lease した v1 page をラップして保持。Hot 部分の pop/push は v1 の tiny_heap_page_pop/free_local を直接叩き、meta/ss_active の整合は v1 に任せる。
- Free もまず v2 の current_page を優先し、範囲外や meta 不一致は従来の C7 free にフォールバック。Superslab/Remote/Stats など Cold 処理は依然 v1 に委譲lease を返却しない簡易版)。
- まだ性能は見ず、v2 の Hot 部分が自前で current_page を握れることだけ確認する段階。
Phase33 以降の選択肢
--------------------
- A) v2 で current_pagefreelist の多ページ対応・ページ返却まで拡張し、Superslab への触れ方を v2 専用に寄せるv1 はページ供給だけの Box にする)。
- B) 当面 v2 は C7 限定のラッパ単一ページ管理のまま据え置き、mid サイズや他クラスの箱に時間を回す。
Phase34: C7-only 専用モードに一旦限定& v2 用 stats 追加
------------------------------------------------------
- 方針: `HAKMEM_TINY_HOTHEAP_V2` は当面 **C7-only ベンチ/実験専用**。Mixed 161024B では v2 OFF 推奨v1 C7_SAFE を使用)。
- v2 専用 statsENV: `HAKMEM_TINY_HOTHEAP_V2_STATS=1`)を追加:
- `alloc_calls / alloc_fast / alloc_lease / alloc_fb_v1`
- `free_calls / free_fast / free_fb_v1`
- destructor で `[HOTHEAP_V2_C7_STATS]` を 1 行 dump。
- これで Mixed 時に「v2 がどこまでヒットしているか」「どれだけ v1 へフォールバックしているか」を切り分ける予定。
- 次フェーズ候補:
- パターンA: Mixed でも v2 のヒット率が高い → 枝/チェックの純コスト削減hot path flattenを検討。
- パターンB: Mixed で v1 fallback が多い → v2 を C7-heavy 専用に割り切る or C7 判定をさらに絞る。
Phase35: v2 をいったん棚上げ(標準は v1+C7_SAFE に集中)
---------------------------------------------------------
- 観測結果: C7-only/Mixed いずれも v2 ON で大幅に劣化alloc_fast がほぼ当たらず、lease→v1 fallback が支配)。
- 運用方針:
- `HAKMEM_TINY_HOTHEAP_V2=0` を標準C7_SAFE v1 がデフォルト)。
- v2 は C7-only の研究用に **明示的に ON** したときだけ使用。Mixed では OFF 推奨。
- 設計メモ:
- 現行 v2 は v1 へのフォールバック前提で、Hot path の枝コストと fallback 多発が主因で負けている。
- 続けるなら current/freelist を完全に自前管理し、Superslab との境界だけ v2 専用に切る再設計が必要。
Phase36+: TinyHotHeap v2 を「Hot Box」として再定義する
====================================================
背景(棚上げからの再出発)
--------------------------
- Phase35 までの v2 は「v1 TinyHeap/C7 SAFE の上に乗るラッパ」であり、
- ページ供給・meta/ss_active/Remote/Stats はすべて v1 に委譲
- current/freelist も最終的には v1 の API を介して触る構造
だったため、**Mixed では構造的に v1 に勝てない** 状態だった。
- Box Theory 的に見ると、
- v2 の Hot 部と v1 TinyHeap の境界が曖昧(箱が二重になっている)
- Cold BoxSuperslab/Tier/Statsとの接点も v1/v2 の両方に分散
しており、「境界 1 箇所」の原則から外れている。
- そこで Phase36 以降は:
> **TinyHeap v2 自体を per-thread Hot Box として再定義し、
> Superslab / Tier / Remote / Stats / Learning をすべて外側の Cold Box に落とす**
という方針に切り替える。
3 つの設計案A/B/C
--------------------
ここからの v2 には、Tiny 層をどこまで巻き取るかで 3 案ある。
1. A案: C5〜C7 を 1 つの TinyHotHeap に統合
- `TinyHeapCtx`per-thread`TinyClassHeap`per-classを定義し、
current/partial/full/page freelist を 1 箱にまとめる標準案。
- Superslab/Warm/Tier/Guard との境界は
- alloc 側: `tiny_heap_refill_slow(th, ci)`
- free 側: `tiny_heap_page_retire_slow(th, page)`
だけに集約する。
2. B案: C7 超ホットレーン + C5/C6 セミホットレーン
- A案の上に「C7 専用 fast lane (`c7_fastlist + c7_current`)」を乗せて、
mimalloc クラスの C7-only 性能を狙う二車線構造。
3. C案: mimalloc 風 Segment+Page+Block をフル導入
- Superslab/Tier/Guard を 1 つの Segment Box として再定義し、
TinyHotHeap v2 の下に「Segment→Page→Block」の階層を敷き直す v3 相当案。
このドキュメントでは **A案TinyHotHeap v2 = Hot Boxを第一候補** とし、
実装を進めやすい形に具体化する。B/C 案は A が安定したあとに検討する。
TinyHotHeap v2 の箱構造A案
------------------------------
Box Theory 観点での分割は次の通り:
- Hot Box: TinyHotHeapBox v2per-thread
- 中身: `TinyHeapCtx` / `TinyClassHeap` / `TinyPageMeta`
- 役割: heap→page→block のみを扱い、**Tiny 層の命令数を支配する唯一のホット層**。
- Cold Box: Superslab/Segment + Tier + Guard + Remote
- 役割: 大きなチャンク管理、Tier/HOT/DRAINING/FREE 状態管理、Remote Queue、Guard/Budget。
- Hot からは `refill_slow` / `page_retire_slow` でしか触らない。
- Policy Box: TinyPolicySnapshot
- 役割: クラスごとの `heap_enabled` / `max_partial_pages` / `warm_cap` などを保持。
- Hot は「現在のスナップショットを読むだけ」で、学習の有無を意識しない。
- Stats Box / Learning Box:
- 役割: page 単位の delta を受け取り、Cold 側で集計・可視化・学習を行う。
- Hot は「page を refill/retire するとき」にだけ delta を渡す。
Hot Box のデータ構造(案)
--------------------------
```c
// ページ内メタデータ(ホット側が見る最小限)
typedef struct TinyPageMeta {
void* free_list; // ページ内の block 単位 LIFO freelist
uint16_t used; // 使用中 block 数
uint16_t capacity; // page あたりの総 block 数
uint16_t class_idx; // Tiny class (C5〜C7)
uint16_t flags; // HOT/PARTIAL/FULL など
void* slab_ref; // Superslab/Segment 側への back pointerCold Box 用)
} TinyPageMeta;
// クラスごとの Hot heap 状態
typedef struct TinyClassHeap {
TinyPageMeta* current; // 直近で使用中の page
TinyPageMeta* partial_head; // 空きあり page のリスト
TinyPageMeta* full_head; // full pageレアイベントのみ参照
} TinyClassHeap;
// TLS ごとの TinyHotHeap コンテキスト
typedef struct TinyHeapCtx {
TinyClassHeap cls[8]; // とりあえず C0〜C7。v2 では C5〜C7 を優先して有効化
} TinyHeapCtx;
```
Hot パス APIalloc/free
-------------------------
### allocHot
```c
void* tiny_heap_alloc_fast(TinyHeapCtx* th, uint32_t ci) {
TinyClassHeap* h = &th->cls[ci];
TinyPageMeta* p = h->current;
// 1. current page から pop
if (likely(p && p->free_list)) {
void* block = p->free_list;
p->free_list = *(void**)block;
p->used++;
return block;
}
// 2. partial list から page を 1 枚取って current に昇格
if (h->partial_head) {
p = h->partial_head;
h->partial_head = p->next; // intrusive list 想定
h->current = p;
// 取り直した current から改めて pop
...
}
// 3. ここで初めて Cold Box に降りる
return tiny_heap_refill_slow(th, ci);
}
```
### freeHot
```c
void tiny_heap_free_fast(TinyHeapCtx* th, void* ptr) {
TinyPageMeta* p = tiny_page_of(ptr); // base から逆算 or side meta
// ページ内 freelist に push
*(void**)ptr = p->free_list;
p->free_list = ptr;
p->used--;
if (unlikely(p->used == 0)) {
// page 完全 free → Cold Box に返却
tiny_heap_page_retire_slow(th, p);
}
}
```
Hot→Cold 境界1 箇所ルール)
------------------------------
- `tiny_heap_refill_slow(th, ci)`
- Warm/Shared/Superslab/Segment/Tier/Guard に対して
「class ci 用の page を 1〜数枚ください」と要求する唯一の関数。
- 返ってきた page 群を `current` または `partial_head` に接続するだけで、
meta->used / ss_active_* / Tier 状態は Cold Box 側が責任を持つ。
- `tiny_heap_page_retire_slow(th, p)`
- `p->used == 0` のときだけ呼ぶ。
- page の返却/再利用Warm Pool への push や Superslab への返却)と、
Cold Stats/Guard の更新をまとめて処理する。
この 2 関数だけが Hot→Cold の境界となるため、
Box Theory の「境界 1 箇所」に近い構造でデバッグ/A/B を行える。
Stats / Learning の配置
----------------------
- Hot Box:
- `TinyPageMeta` に軽量なローカルカウンタのみを持たせる(例: `uint16_t local_allocs;` など)。
- `tiny_heap_refill_slow` / `tiny_heap_page_retire_slow` のタイミングで
page 単位の delta を Cold Stats Box に flush する。
- Cold Stats Box:
- Superslab/Segment/class 単位の累積 stats を保持。
- Learning Box からのみ読まれ、Hot Box は直接読まない。
- Learning Box:
- 一定周期で Stats から `TinyPolicySnapshot` を更新し、
`heap_enabled[class]` / `max_partial_pages[class]` / `warm_cap[class]` を調整する。
- Hot Box は `ci` 決定後に snapshot を読むだけで、
Learning の ON/OFF によって命令数が変わらないようにする。
ENV / Gate / A/B 方針v2 再開時のルール)
-----------------------------------------
- ENV:
- `HAKMEM_TINY_HOTHEAP_V2` … v2 Hot Box を有効化(デフォルト 0
- `HAKMEM_TINY_HOTHEAP_CLASSES` … v2 適用クラスの bitmask。初期は C7-only (=0x80)、
様子を見ながら 0xC0(C6+C7)→0xE0(C5〜C7) へ広げる。
- Route Snapshot:
- `g_tiny_route_class[ci]``TINY_ROUTE_HOTHEAP_V2 / TINY_ROUTE_HEAP_V1 / TINY_ROUTE_LEGACY` を設定。
- Gate は `size→class→route` を 1 LUT + 1 分岐で決める薄い front を維持する。
- A/B:
- C7-only: `HAKMEM_TINY_HOTHEAP_V2=1 HAKMEM_TINY_HOTHEAP_CLASSES=0x80` で v2 経路に切り替え、
`=0` で v1 C7_SAFE に戻せるようにする。
- Mixed 161024B: 当面は v1 C7_SAFE を基準とし、C7-only が安定してから C6/C5 を昇格させる。
将来の C 案Segment+Page+Blockへの橋渡し
------------------------------------------
- 上記 A 案TinyHotHeap v2 = Hot Boxは、
mimalloc 風の Segment+Page+Block 構造C 案)の **Hot 部分** とほぼ一致している。
- 今後 C 案へ進める場合も、
- Hot Box の API (`tiny_heap_alloc_fast/free_fast/refill_slow/page_retire_slow`) は変えず、
- Cold Box 側で Superslab/Tier/Guard を「Segment Box」として整理する
ことで、段階的に v3 へ移行できる。
- このため、Phase36 以降の v2 実装は **「C 案の Hot 部だけを先に完成させる」** 方針で進める。
次にやること(実装タスクの箱化)
--------------------------------
- docs/design/TINY_HOTHEAP_V2_IMPLEMENTATION_GUIDE.md に、
実装手順C7-only → C5〜C7 への拡張)と関数名/API の具体案をまとめる。
- CURRENT_TASK.md に Phase36 として「TinyHotHeap v2 再定義Hot Box 化)」を追加し、
v2 の現状ステータスと今後のタスク窓口をこのドキュメントに集約する。
Phase36 (C7-only Hot Box 暫定実装)
----------------------------------
- 状態: v2 は C7-only 実験箱のまま。デフォルトは `HAKMEM_TINY_HOTHEAP_V2=0`v1 C7_SAFE が標準)。
- 実装ポイント:
- TLS 取得で全 class の storage_page を resetstride 初期化。page node は storage を優先し、足りなければ calloc。
- Hot パス: `tiny_hotheap_v2_alloc/free` が current_page → partial → refill を処理し、lease した v1 page を pop/push して meta/ss を同期。used==0 は retire_slow でリセット。
- Cold 境界: `tiny_hotheap_v2_refill_slow``tiny_heap_c7_lease_page_for_v2()` 経由で 1 枚借りる)と `tiny_hotheap_v2_page_retire_slow`返却resetの 2 箇所のみ。Superslab/Tier/Stats は v1 が保持。
- Route/Gate: Route Snapshot に `TINY_ROUTE_HOTHEAP_V2` を設定し、C7 直線パス/汎用パスとも route==v2 のときだけ HotHeap v2 を呼ぶ。その他は v1 → legacy slow へフォールバック。
- 今後: v2 は C7-only bench 用の A/B 前提。Mixed では v2 OFF 推奨。より本格的に進めるなら、v1 依存の lease/pop/push を減らし、Cold Stats Box 経由の更新に寄せる必要がある。