Files
hakmem/docs/design/TINY_HOTHEAP_V2_IMPLEMENTATION_GUIDE.md
2025-12-09 21:50:15 +09:00

34 KiB
Raw Blame History

TinyHotHeap v2 Implementation Guide (A案 → C案の入口)

日付: 2025-12-05
対象: 実装担当 AI / 開発者向け指示書
関連ドキュメント:

  • docs/analysis/TINY_HEAP_V2_DESIGN.md (Phase36+ 設計 / A/B/C 案・箱構造)
  • docs/analysis/TINY_HEAP_BOX_DESIGN.md (v1 TinyHeapBox / C7 HotBox)
  • docs/analysis/C7_HOTBOX_DESIGN.md (既存 C7 HotBox 設計)
  • CURRENT_TASK.md (Phase36 状況メモ)
  • ステータス: Phase36 で C7-only の Hot 部current_page+freelistを v2 で自前管理する暫定実装を投入済み。Superslab/Tier/Stats は v1 に委譲する lease/refill/retire 境界で A/B できる状態。

Phase65/66: C7-only フェーズ完了サマリ

  • C7-only / Mixed 161024B の長尺プロファイルws=400, iters=1M, PROFILE=C7_SAFEで、v2 ON/OFF いずれも HEAP_STATS[7].slow≈1・refill≈1 に収束し、性能は v1 比で ±5% 以内(むしろ数%プラス)。
  • 短尺ベンチ20k/ws=64で見える refill≒50 はウォームアップ由来と判断され、本命プロファイルに影響しないことを確認。
  • 運用上の扱い:
    • デフォルト構成では HAKMEM_TINY_HOTHEAP_V2=0C7 SAFE v1を維持。
    • C7-only の bench/pro 用プロファイルでは HAKMEM_TINY_HOTHEAP_V2=1 HAKMEM_TINY_HOTHEAP_CLASSES=0x80 を opt-in 推奨とし、v2 を本命候補の Hot Box として使う。
    • C6 v2 は構造こそ通電したものの perf 未達のため、引き続き研究箱classes=0x40/0xC0 を明示したときだけ有効)に留める。

Phase60 速報C7-only 空ページ保持ポリシー)

  • 追加したもの:
    • max_partial_pagesC7 デフォルト 2partial_count を Hot Box に追加し、free で used==0 の page は一度 partial へ温存。上限超のみ retire を呼ぶ。
    • partial push/pop/peak と retire_v2 を v2 stats に追加(HAKMEM_TINY_HOTHEAP_V2_STATS=1 で確認)。
  • 初期ベンチ:
    • C7-only v2 ON で slow_prepare は alloc_lease と一致≈48 回。live ブロックが多く page が空にならないため partial/retire は未発火。
    • Mixed でも v2 ON は +2〜3% だが slow≈refill(42)。空ページを作れる workload またはページ容量/lease の見直しが次課題。
  • 運用: v2 は引き続き研究箱(デフォルト OFF。C7-only で安定後に Mixed への適用を検討する。

0. ゴールと非ゴール

  • ゴール
    • TinyHotHeap v2 を per-thread Hot Box として実装し、C7-only から段階的に C6/C5 へ拡張できる骨格を作る。
    • Hot Box 側は heap→page→block のみ を扱い、Superslab/Tier/Remote/Stats/Learning との境界は
      • alloc 側: tiny_hotheap_v2_refill_slow(...)
      • free 側: tiny_hotheap_v2_page_retire_slow(...) の 1 箇所に集約するBox Theory: 境界 1 箇所)。
    • A/B: HAKMEM_TINY_HOTHEAP_V2 / HAKMEM_TINY_HOTHEAP_CLASSES / Route Snapshot により、
      C7-only で v1 C7_SAFE と v2 Hot Box を即時切り替え可能にする。
  • 非ゴール(このガイドの範囲外)
    • Superslab/Tier/Guard/Remote のフル再設計C 案の Segment+Page+Block までは行かない)。
    • C0〜C4 や mid/large サイズ帯の設計変更。
    • 学習層 (ACE/ELO) の仕様変更。Hot path は従来どおり Snapshot を読むだけとする。

1. 箱の対応関係(概念 → 既存型へのマッピング)

設計ドキュメントでは便宜的に:

  • TinyPageMeta / TinyClassHeap / TinyHeapCtx

という名称で説明しているが、実装では既に存在する v2 skeleton を利用する:

  • ファイル: core/box/tiny_hotheap_v2_box.h
    • tiny_hotheap_page_v2 … 概念上の TinyPageMeta
    • tiny_hotheap_class_v2 … 概念上の TinyClassHeap
    • tiny_hotheap_ctx_v2 … 概念上の TinyHeapCtx

実装では この既存ヘッダを Hot Box の中核 とし、
他のファイルからは基本的に tiny_hotheap_v2_* API 経由でしか触らない。


2. Hot Box 実装ステップ

Step 2-1: 型の見直しと拡張 (core/box/tiny_hotheap_v2_box.h)

目的: tiny_hotheap_page_v2 / tiny_hotheap_class_v2 / tiny_hotheap_ctx_v2
Phase36 設計の A 案Hot Boxに揃える。

作業指示:

  • tiny_hotheap_page_v2
    • すでに freelist/used/capacity/base/meta/ss/slab_idx/next を持っているので、
      ページ内 freelist と Cold 側リンク用の最小限メタ として継続利用する。
    • 必要であれば flagsHOT/PARTIAL/FULLなどの軽量フラグを追加してよい。
    • lease_page フィールドは「v1 page への橋渡し用」なので、
      Phase1 では残しておき、将来的には A 案が安定した時点で削除/無効化を検討する。
  • tiny_hotheap_class_v2
    • current_page / partial_pages / full_pages が A 案の TinyClassHeap に相当する。
    • storage_page は Phase32 の「C7 1 枚 lease」専用だが、Phase1 では
      • C7-only: storage を current_page として使う
      • 複数ページ化後: storage は「最初の page」または「固定 1 枚キャッシュ」として扱う 形で再利用してよい(完全削除は後のフェーズでも可)。
    • stride はクラスごとの block サイズ(バイト数)として維持。
  • tiny_hotheap_ctx_v2
    • cls[TINY_HOTHEAP_MAX_CLASSES] の配列で全 Tiny class をカバー済み。
      Phase1 では C7-only を有効 にし、C6/C5 は未使用のままでよい。

このステップでは まだ関数実装をいじらず、型だけを A 案に整合 させる。

Step 2-2: TLS 取得 API の実装 (core/hakmem_tiny.c など)

目的: Hot Box の入口を 1 箇所に揃える。

作業指示:

  • tiny_hotheap_v2_tls_get() の実装:
    • g_tiny_hotheap_ctx_v2__thread)を lazily malloc/calloc し、memset でゼロ初期化。
    • 初期化時に tiny_hotheap_v2_page_reset(&cls[i].storage_page) など、
      最低限のリセットを行うBox 内に初期化を閉じ込める)。
    • 返り値は tiny_hotheap_ctx_v2*。以降、Hot Box の関数は 必ず ctx を通して状態にアクセス する。

Step 2-3: alloc Hot パスの実装 (tiny_hotheap_v2_box.h)

目的: C7-only の tiny_hotheap_v2_alloc()page 内 freelist + current/partial だけで完結させる。

推奨構造:

  • static inline void* tiny_hotheap_v2_alloc_fast(tiny_hotheap_ctx_v2* ctx, uint8_t class_idx);
  • void* tiny_hotheap_v2_alloc(uint8_t class_idx); は Gate から呼ばれる薄いラッパとして、
    • ctx = tiny_hotheap_v2_tls_get();
    • return tiny_hotheap_v2_alloc_fast(ctx, class_idx); だけを行う。

Hot パスのロジックC7 専用版):

  • tiny_hotheap_class_v2* hc = &ctx->cls[class_idx];
  • 優先順位:
    1. hc->current_page があり、その freelist が非 NULL → 1 block pop して used++
    2. hc->partial_pages リストがあれば 1 枚取り出し、current_page に昇格させてから pop。
    3. どちらも空なら tiny_hotheap_v2_refill_slow(ctx, class_idx) へ降りる。

注意:

  • Phase1 では C7-only を対象とし、class_idx == 7 以外は即座に v1 経路へフォールバックしてよい。
  • tiny_hotheap_v2_refill_slow は次節の Cold Box との境界関数として後から実装する。

Step 2-4: free Hot パスの実装 (tiny_hotheap_v2_box.h)

目的: C7-only の tiny_hotheap_v2_free() を page 内 freelist push + empty 判定だけにする。

推奨構造:

  • static inline void tiny_hotheap_v2_free_fast(tiny_hotheap_ctx_v2* ctx, uint8_t class_idx, void* p, struct TinySlabMeta* meta);
  • void tiny_hotheap_v2_free(uint8_t class_idx, void* p, void* meta); は Gate 用ラッパとして、
    • ctx = tiny_hotheap_v2_tls_get();
    • tiny_hotheap_v2_free_fast(ctx, class_idx, p, (struct TinySlabMeta*)meta); のみ行う。

Hot パスのロジックC7 専用版):

  • tiny_hotheap_page_v2* page = tiny_hotheap_v2_page_of(ctx, class_idx, p, meta);
    • Phase1 では meta/base/slab_idx から page を特定するヘルパを 1 箱に閉じ込める(実装場所はこのヘッダ or 専用 C ファイル)。
  • *(void**)p = page->freelist; page->freelist = p; page->used--;
  • page->used == 0 になったら tiny_hotheap_v2_page_retire_slow(ctx, class_idx, page); を呼ぶ。

備考:

  • Phase1 では Remote Queue / cross-thread free は従来経路にフォールバック してよい。
    具体的には:
    • 所有スレッド以外からの free であれば v1 の remote 経路を即呼び出す。
    • 所有スレッド判定には既存の TLS/owner 情報を使うOwnership Box を汚さない)。

3. Cold Box 境界実装ステップ

Step 3-1: refill_slowv1 TinyHeap / Superslab からのページ供給)

目的: Hot Box から見て 唯一の「ページ供給」入口 を 1 箇所に集約する。

作業指示:

  • 関数案(実装ファイルは core/hakmem_tiny.c か、専用の core/box/tiny_hotheap_v2_cold_box.h でもよい):
tiny_hotheap_page_v2*
tiny_hotheap_v2_refill_slow(tiny_hotheap_ctx_v2* ctx, uint8_t class_idx);
  • Phase1 では実装を 既存 v1 TinyHeap/C7 SAFE に橋渡しするだけ に留める:
    • tiny_heap_c7_lease_page_for_v2() など、既存の C7 lease API を利用して 1 枚 page をもらう。
    • 取得した TinySlabMeta* / SuperSlab* / base / capacitytiny_hotheap_page_v2 にコピーし、current_page or partial_pages に追加。
    • freelist の初期化は Hot Box 側で実施 し、以降の pop/push は v2 が握るv1 に再委譲しない)。

Step 3-2: page_retire_slow全 free になったページの返却)

目的: ページが empty になった瞬間だけ Cold Box に touch する。

作業指示:

  • 関数案:
void
tiny_hotheap_v2_page_retire_slow(tiny_hotheap_ctx_v2* ctx,
                                 uint8_t class_idx,
                                 tiny_hotheap_page_v2* page);
  • Phase1 の挙動:
    • page を current_page / partial_pages / full_pages から unlink。
    • v1 側に「ページ free」イベントとして渡す:
      • Superslab / Warm Pool / Tier / Guard の扱いは v1 に任せるv2 は触らない)。
      • Cold Stats Box があれば、ここで delta を flush する。
  • 将来フェーズ:
    • Warm Pool / Superslab への返却ロジックを v2 専用に寄せることで、
      C 案Segment+Page+Blockへの移行パスとして利用する。

4. Gate / Route / ENV の配線ステップ

Step 4-1: Route Snapshot への v2 統合

作業指示:

  • ファイル: core/box/tiny_route_box.h 付近(g_tiny_route_class[] の決定ロジック)。
  • ENV:
    • HAKMEM_TINY_HOTHEAP_V2 / HAKMEM_TINY_HOTHEAP_CLASSES を読み、
      • bit7 が立っていれば g_tiny_route_class[7] = TINY_ROUTE_HOTHEAP_V2;
      • それ以外は従来通り TINY_ROUTE_HEAP_V1 / TINY_ROUTE_LEGACY を設定する。
  • これにより front 側は class_idx 決定後に
    • route = g_tiny_route_class[class_idx];
    • switch (route) { HOTHEAP_V2 / HEAP_V1 / LEGACY } だけで経路を決定できる。

Step 4-2: front からの呼び出しmalloc_tiny_fast / free_tiny_fast

作業指示:

  • ファイル: core/hakmem_tiny.c + 各種 tiny_alloc_fast*.inc.h / tiny_free_fast_v2.inc.h
  • C7-only モードの Gate で:
    • route == TINY_ROUTE_HOTHEAP_V2 かつ size が C7 クラスにマップされる場合にのみ tiny_hotheap_v2_alloc(7) / tiny_hotheap_v2_free(7, p, meta) を呼ぶ。
    • それ以外は従来通り v1 TinyHeap / legacy 経路へフォールバックする。
  • Free 側では:
    • C7 ptr かつ Route=HOTHEAP_V2 かつ owner==self の場合のみ tiny_hotheap_v2_free を試す。
    • 条件に当てはまらない場合は Remote Queue / v1 free 経路にすぐ落とす。

5. 可視化・Fail-Fast・A/B の運用

Step 5-1: v2 専用 Stats の追加(任意だが推奨)

作業指示:

  • 既存の tiny_stats_box / tiny_class_stats_box に倣って、
    • alloc_calls / alloc_fast / alloc_slow_refill / alloc_fb_v1
    • free_calls / free_fast / free_fb_v1 のような v2 専用カウンタを追加。
  • ENV:
    • HAKMEM_TINY_HOTHEAP_V2_STATS=1 のときだけ destructor で 1 行ログを出す。

Step 5-2: Fail-Fast ポリシー

作業指示:

  • 範囲外 ptr / page_of 失敗 / meta 不整合など、Hot Box の前提が壊れた場合は即 abort する。
  • v2 実装中は「誤魔化しのフォールバック」を避け、
    問題が出たら Fail-Fast で原因を特定するAGENTS.md の方針通り)。

Step 5-3: 推奨ベンチコマンド(実装後に利用)

実装完了後、以下のようなプロファイルで A/B を行う(ここでは指示のみ、実行は別フェーズ):

  • C7-only:
    • v1 基準: HAKMEM_BENCH_C7_ONLY=1 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_HOTHEAP_V2=0 ...
    • v2: HAKMEM_BENCH_C7_ONLY=1 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_HOTHEAP_V2=1 HAKMEM_TINY_HOTHEAP_CLASSES=0x80 ...
  • Mixed 161024B:
    • 当面は v1 C7_SAFE を基準に残し、v2 は C7-only が十分安定してから評価する。

6. 実装順序のまとめ(チェックリスト)

  • 型整理: tiny_hotheap_page_v2 / tiny_hotheap_class_v2 / tiny_hotheap_ctx_v2 を Phase36 A 案に整合させる。
  • TLS 初期化: tiny_hotheap_v2_tls_get() を実装し、Hot Box 内に状態を閉じ込める。
  • alloc Hot パス: tiny_hotheap_v2_alloc_fast() / tiny_hotheap_v2_alloc() を C7-only で実装。
  • free Hot パス: tiny_hotheap_v2_free_fast() / tiny_hotheap_v2_free() を C7-only で実装。
  • Cold 境界: tiny_hotheap_v2_refill_slow() / tiny_hotheap_v2_page_retire_slow() を v1 TinyHeap への橋渡しとして実装。
  • Route/Gate: tiny_route_box と front を更新し、C7-only で v1/v2/legacy を A/B 切り替え可能にする。
  • Stats/Fail-Fast: v2 専用 stats と assert/abort を追加し、問題発生時にすぐ気付けるようにする。

このガイドは「箱」と「境界」の位置だけを固定するものであり、
細かな最適化C7 超ホットレーン / C6/C5 への拡張 / C 案への橋渡し)は Phase36 以降のサブフェーズで順次詰める前提とする。


7. Phase37: C7 current_page ポリシー修正指示書

ベンチ結果サマリ(現状 v2 の問題)

Release, PROFILE=C7_SAFE での観測:

  • C7-only (ws=64, iters=20k)
    • v2 OFF: 39.64M ops/s, HEAP_STATS[7] fast=11015 slow=1
    • v2 ON : 26.89M ops/s, HEAP_STATS[7] fast=97 slow=32758(ほぼ全て slow_prepare
  • Mixed 161024B (ws=256, iters=20k)
    • v2 OFF: 38.78M ops/s, fast=5691 slow=1
    • v2 ON : 33.26M ops/s, fast=141 slow=16654

結論:

  • v2 ON では current_page がほぼ活きておらず、毎回 slow_prepare に落ちている
  • 現状の v2 を運用で使うのは危険なため、当面のデフォルトは v2 OFF (C7_SAFE + HOTHEAP_V2=0) を維持する。

この Phase37 の目的

  • 目的:
    • C7-only で current_page をきちんと保持・再利用し、slow_prepare をほぼゼロに抑える
    • v1 C7 SAFE / TinyHeapBox の current_page ポリシーを v2 Hot Box に移植する。
  • 非ゴール:
    • C6/C5 への展開。
    • Cold Box (Superslab/Tier/Remote/Stats) の構造変更。

ざっくりタスク一覧

  • v2 専用 current_page デバッグ統計の追加
  • refill_slow → current_page セット手順の見直し
  • free 側で current_page を再利用するポリシーの導入
  • retire 条件の見直しempty でもすぐ返さない)
  • v1 C7 SAFE current_page ポリシーとの比較・差分吸収
  • C7-only / Mixed ベンチで slow_prepare を再確認

以下、それぞれの具体指示。

Step 7-1: current_page デバッグ統計の追加

目的:

  • v2 が「どこで current_page を失って slow_prepare に落ちているか」を1 箇所で見える化する。

作業指示:

  • 参考: docs/analysis/TINY_HEAP_BOX_DESIGN.md にある C7 current_page statsprepare_calls など)。
  • 新規 struct を追加(場所は tiny_hotheap_v2_box.h か、専用 stats ファイルでも良い):
    • uint64_t prepare_calls;
    • uint64_t prepare_with_current_null;
    • uint64_t prepare_from_partial;
    • uint64_t free_made_current;
    • uint64_t page_retired;
  • ENV HAKMEM_TINY_HOTHEAP_V2_STATS=1 のときのみカウンタを更新・dump する。
  • カウントポイント:
    • tiny_hotheap_v2_alloc_fast 内:
      • entry ごとに prepare_calls++
      • hc->current_page==NULL のとき prepare_with_current_null++
      • partial から current に昇格したとき prepare_from_partial++
    • tiny_hotheap_v2_free_fast 内:
      • free で page が current になる/保持される場合 free_made_current++
    • tiny_hotheap_v2_page_retire_slow 内:
      • page_retired++

Step 7-2: refill_slow → current_page セットの修正

目的:

  • refill 後に必ず「C7 用の current_page が 1 枚セットされる」ようにする。

作業指示:

  • tiny_hotheap_v2_refill_slow の振る舞いを以下のように揃える:
    1. C7 用 lease APItiny_heap_c7_lease_page_for_v2() など)から 1 枚 page を取得。
    2. tiny_hotheap_page_v2 へ:
      • base / capacity / meta / ss / slab_idx / stride をコピー。
      • freelist を page 内 carving で初期化pop は v2 が握る)。
    3. tiny_hotheap_class_v2* hc = &ctx->cls[7]; に対して:
      • hc->current_page が NULL なら 必ずここで current_page にセット
      • 既に current がある場合は hc->partial_pages に push。
  • refill 後に hc->current_page が NULL のままにならないよう、Fail-Fast assert を入れても良い(デバッグ時)。

Step 7-3: free 側で current_page を維持・再利用する

目的:

  • C7-only では「同じ page 内で alloc/free が揺れる」ケースが多いため、
    free 側で current_page をなるべく保持・再利用する。

作業指示:

  • tiny_hotheap_v2_free_fast で、以下のようなポリシーを入れるv1 TinyHeapBox SAFE と同等イメージ):
    • page->used > 0 かつ page->freelist != NULL の page は current_page 候補
    • hc->current_page が NULL の場合は、free した page を current に据え直す。
    • hc->current_page が他の page を指していても、C7-only フェーズでは「free された page に空きがあるなら current に切り替える」実験をして良い(後で safe なポリシーに絞る)。
  • これにより、C7-only ベンチで prepare_calls に対して prepare_with_current_null がほぼ 0 に近づくことを期待する。

Step 7-4: retire 条件の見直し

目的:

  • v2 ON で empty page を早々に Cold Box に返しすぎると、current_page を毎回失って slow_prepare 連発になる。

作業指示:

  • tiny_hotheap_v2_page_retire_slow を呼ぶ条件を見直す:
    • Phase37 では「C7-only ベンチでは page が empty になってもすぐには返さない」実験を許容する。
    • 具体案:
      • page->used == 0 でも、まず hc->partial_pages の一員として残す(一定枚数を超えたら返却)。
      • C7-only フェーズでは max_partial_pages を 1〜2 に固定して試す。
  • 本番運用向けには、後続フェーズで安全側にチューニングし直す前提で、「今は slow_prepare を殺すことを最優先」にしてよい。

Step 7-5: v1 C7 SAFE current_page ポリシーとの比較

目的:

  • 「すでにうまく動いている v1 C7 SAFE / TinyHeapBox の current_page 設計」を v2 に再利用する。

作業指示:

  • 参照:
    • docs/analysis/TINY_HEAP_BOX_DESIGN.md
    • docs/analysis/C7_HOTBOX_DESIGN.md
  • この2つから:
    • alloc 時: prepare_calls=1 に抑えるための current 固定ロジック
    • free 時: empty page の扱いULTRA/SAFE の違い) を抜き出し、v2 Hot Box の alloc_fast / free_fast / page_retire_slow に反映する。
  • 差分:
    • v1 TinyHeapBox は meta/ss_active 更新も Hot 側が握っていたが、v2 では Cold Stats Box に寄せる方針。
    • そのため meta/active の更新部分だけは v1 と同じにせず、current_page に関わるポリシー部分だけをコピー する。

Step 7-6: 再ベンチと判定基準

作業指示:

  • C7-only (ws=64, iters=20k, PROFILE=C7_SAFE) で:
    • v2 OFF: 39〜40M ops/s既存ベースライン
    • v2 ON : 目標として 少なくとも v2 OFF と同等 ±5% 以内 を目指す。
    • HEAP_STATS[7] fast≈11015 slow≈1 に戻ることを確認。
    • v2 current_page stats では:
      • prepare_calls に対して prepare_with_current_null が ≪1% 程度
  • Mixed 161024B (ws=256, iters=20k) では:
    • v2 OFF の 38〜39M ops/s に対して ±5% 以内 を許容範囲とする。
    • C7-only が十分安定・同等以上であれば、Mixed の調整は後続フェーズで良い。

判定:

  • 上記基準を満たせば「Phase37: current_page ポリシー修正完了」とし、
    次フェーズで C6/C5 への拡張や C7 超ホットレーンB案を検討できる。
  • 満たせない場合は v2 を引き続き デフォルト OFF / 研究用箱 としたまま、
    current_page / retire ポリシーの再修正に戻る。

8. Phase58: TinyColdIface 導入後の課題整理と次の実装指示

現状ステータスPhase58 時点)

  • core/box/tiny_cold_iface_v1.hTinyColdIface が導入され、v1 TinyHeap の
    • tiny_heap_prepare_page()refill_page(cold_ctx, class_idx)
    • tiny_heap_page_becomes_empty()retire_page(cold_ctx, class_idx, page) としてラップ済み。Hot Box 側からはこの IF だけを見れば Superslab/Tier/Stats に触れられる状態になっている。
  • C7 v2 の Cold 境界は次のように接続済み:
    • tiny_hotheap_v2_refill_slow():
      • TinyColdIface cold = tiny_cold_iface_v1();
      • tiny_heap_ctx_t* cold_ctx = tiny_heap_ctx_for_thread();
      • ipage = cold.refill_page(cold_ctx, class_idx) で v1 から tiny_heap_page_t を 1 枚借りる。
      • v2 側の tiny_hotheap_page_v2 ノードを確保し、base/capacity/meta/ss/slab_idx をコピー。
      • freelist が無い場合は v2 が used=0 にリセットし、tiny_hotheap_v2_build_freelist() で carve する。
      • freelist がある場合は lease_page->meta->freelist を v2 freelist で更新。
      • hc->current_page に必ず freelist 付きページが入るよう Fail-Fast チェックを追加。
    • tiny_hotheap_v2_page_retire_slow():
      • v2 の current_page / partial_pages / full_pages から対象ページを unlink。
      • cold.retire_page(cold_ctx, class_idx, page->lease_page) を呼んで v1 側へ返却。
      • storage_page 以外は free(page)、storage は tiny_hotheap_v2_page_reset() で初期化。
  • TLS 初期化 (tiny_hotheap_v2_tls_get()) では、tiny_hotheap_ctx_v2calloc し、各クラスの storage_page を reset、stridetiny_stride_for_class(i) で事前設定するようにした。
  • v2 Stats:
    • g_tiny_hotheap_v2_c7_*alloc_calls/route_hits/alloc_fast/alloc_refill/alloc_fallback_v1/free_*cold_refill_fail / cold_retire_calls を追加し、HAKMEM_TINY_HOTHEAP_V2_STATS=1 で destructor ダンプ。

残っている問題(なぜ v2 ON で SEGV するか)

  • tiny_hotheap_v2_try_pop() は依然として v1 TinyHeap の page API に強く依存している:
    • candidate->lease_pageipage として取り出し、
      • v1hcls->current_page = ipage;
      • tiny_heap_page_pop(v1hcls, 7, ipage);
      • 必要なら tiny_heap_page_mark_full(v1hcls, ipage); を呼んだうえで、
      • candidate->freelist = ipage->free_list;
      • candidate->used = ipage->used; と v2 側 state にもコピーしている。
  • これは Box Theory 的には

    Hot Box (v2) が Cold Box (v1 TinyHeap) の内部状態 (free_list/used) を直接操作している ことになり、境界が二重になっている。

  • refill で v2 が独自に carve した freelist と、v1 の tiny_heap_page_pop() が更新する free_list/used/meta がずれていくと、
    • 無効な ipage->base / capacity / meta を deref したり
    • v2 の candidate->freelist が壊れた状態で次の pop に進んでしまう 可能性がある。現状の SIGSEGV はこのあたりの不整合に起因していると考えられる。

Phase58 でやるべきこと(実装指示)

8-1. tiny_hotheap_v2_try_pop() から v1 依存を外し、v2 freelist だけで完結させる

目的:

  • Hot Box v2 は「自分の freelist を自分で pop する箱」とし、Cold Box v1 には refill/retire でしか触れないようにする。

作業指示:

  • tiny_hotheap_v2_try_pop() を次の方針で書き換える:
    • 引数から tiny_heap_class_t* v1hcls を削除し、TinyColdIface / v1 TinyHeap の型に依存しない形にする。

    • candidate から直接 freelist を pop:

      static inline void* tiny_hotheap_v2_try_pop(tiny_hotheap_page_v2* candidate,
                                                  TinyHeapClassStats* stats,
                                                  int stats_on) {
          if (!candidate || !candidate->freelist || candidate->used >= candidate->capacity) {
              return NULL;
          }
          void* user = candidate->freelist;
          candidate->freelist = *(void**)user;
          candidate->used++;
          if (__builtin_expect(stats != NULL, 0)) {
              atomic_fetch_add_explicit(&stats->alloc_fast_current, 1, memory_order_relaxed);
          }
          if (stats_on) {
              atomic_fetch_add_explicit(&g_tiny_hotheap_v2_c7_alloc_fast, 1, memory_order_relaxed);
          }
          return tiny_region_id_write_header(user, 7);
      }
      
    • v1 TinyHeap (tiny_heap_page_pop / tiny_heap_page_mark_full / v1hcls->current_page など) の呼び出しは完全に削除する。

    • candidate->lease_pageCold 側に返すための token として保持するだけにし、Hot path では一切触らない。

8-2. Cold 境界 (refill_page / retire_page) を「イベント 2 箇所だけ」に限定する

目的:

  • Hot Box v2 と Cold Box v1 の境界を「page を借りる」「page を返す」の 2 箇所だけにし、meta/used/freelist の二重管理をやめる。

作業指示:

  • tiny_hotheap_v2_refill_slow():
    • TinyColdIface.refill_page() から返ってきた tiny_heap_page_t* ipage については、
      • base / capacity / slab_idx / meta / ss など geometry 情報だけ を読む。
      • ipage->free_list / ipage->used は Hot path では信頼しないv2 が自前で carve した freelist を使う)。
    • v2 側の tiny_hotheap_page_v2 に freelist を構築したら、以降の alloc/free は v2 freelist だけで完結させる。
    • どうしても meta に初期値を書き戻す必要がある場合は、Cold Stats Box 経由の更新に寄せる(直接 meta->used に触らない方向を優先)。
  • tiny_hotheap_v2_page_retire_slow():
    • page->used == 0 かつ v2 内のリストから unlink した段階でのみ TinyColdIface.retire_page() を呼ぶ。
    • 返却後は page->lease_page を含めて v2 側 state を破棄または reset し、同じ token を Hot path から再利用しない。

8-3. 安全側のガードと Stats での確認ポイント

目的:

  • v2 を再度 ON にした際、segv や Cold IF への過剰依存がないかをすぐに検知できるようにする。

作業指示:

  • tiny_hotheap_v2_alloc() / tiny_hotheap_v2_free() の入口で:
    • class_idx != 7 は即 NULL or fallbackC7-only 実験箱のまま)。
    • v2ctx == NULL / hc == NULL / hc->stride == 0 などは Fail-Fastabortで早期検出。
  • tiny_hotheap_v2_try_pop():
    • candidate->capacity == 0candidate->used > candidate->capacity の場合は即 NULL を返す(デバッグビルドでは 1 回だけログ)。
  • Stats:
    • C7-only ベンチ (ws=64, iters=20k, PROFILE=C7_SAFE) で:
      • v2 OFF: 既存どおり HEAP_STATS[7] fast≈11015 slow≈1
      • v2 ON:
        • alloc_fast が 1 回目の page refill 後に route_hits と同程度まで増えていくこと。
        • cold_refill_fail が常に 0 付近であること。
        • alloc_fallback_v1 / free_fallback_v1 が 0〜ごく少数に収まっていること。

備考

  • Phase58 の目的は 「TinyColdIface を導入したうえで、v2 Hot Box と v1 TinyHeap の境界を 2 箇所 (refill/retire) に絞ること」 であり、 この段階では性能よりも安定性segv しないこと)と Box 境界の明確化を優先する。
  • C7-only で v2 が v1 と同等レベルで安定したら、次フェーズで C6/C5 への拡張や perf チューニング(分岐削減・命令数削減)に進む。

9. Phase60/61: C7-only フェーズ完了と次のターゲット (C6 拡張) メモ

現状まとめC7 v2 フェーズの着地)

  • C7-only:
    • v2 OFF (C7 SAFE v1) ≈ 42M ops/s / slow≈1。
    • v2 ON (C7-only, classes=0x80) ≈ 50M ops/s / slow≈40〜50 → 空ページ保持ポリシー導入前の状態。
    • 空ページ保持ポリシー導入 (max_partial_pages / partial_count 追加) 後も、C7-only ではページが実質 full のまま回るため partial/retire はほぼ発火せず、slow は主に refill 回数として現れる。
  • Mixed 161024B:
    • 短尺 (ws=256, iters=20k, PROFILE=C7_SAFE):
      • v2 OFF: ≈43.3M ops/s, HEAP_STATS[7] fast=5691 slow=1。
      • v2 ON: ≈44.6M ops/s約 +3%, HEAP_STATS[7] fast=5691 slow=1, fail/fallback=0。
    • 長尺 (ws=400, iters=1M, PROFILE=C7_SAFE):
      • v2 OFF: ≈41.6M ops/s, HEAP_STATS[7] fast≈283k slow=1。
      • v2 ON: ≈42.4M ops/s約 +2%, HEAP_STATS[7] fast≈283k slow=1, fail/fallback=0。
  • 境界:
    • Cold IF (TinyColdIface) 経由の refill/retire は安定しており、cold_refill_fail / fallback_v1 は 0 に張り付き。
    • C7 v2 の Hot パスは tiny_hotheap_page_v2.freelist のみで完結し、v1 TinyHeap の page/pop は参照しない構造になった。

結論C7 フェーズ)

  • C7 v2 Hot Box は:
    • C7-only / Mixed / 短尺・長尺のいずれでも v1 C7 SAFE を上回るスループットを達成。
    • slow_prepare は v2 OFF/ON ともに 1長尺またはワークロード由来の refill 回数程度に収まり、Cold IF の異常ではない。
    • fail/fallback=0 で、v2→v1 フォールバックに頼らずに Hot Box 単体で完結できている。
  • 運用上の位置づけ:
    • 標準デフォルト: 引き続き HAKMEM_TINY_HOTHEAP_V2=0(どの環境でも即 v1 C7 SAFE に戻せるようにする)。
    • bench/研究プロファイル: C7 クラスに限り HAKMEM_TINY_HOTHEAP_V2=1 / HAKMEM_TINY_HOTHEAP_CLASSES=0x80 を推奨設定とし、C7 v2 を本命候補として扱う。

次のターゲット候補 (C6/C5 拡張の入り口)

  • 目的:
    • TinyHotHeap v2 の設計ゴールどおり、C7-only から C6/C5 へ段階的に適用範囲を広げて、Mixed 161024B 全体の性能を押し上げる。
  • Gate/Policy 側の前提:
    • PolicySnapshot / Route LUT に tiny_heap_version[class_idx] または HAKMEM_TINY_HOTHEAP_CLASSES のマスクを持ち、
      • C7: まず v2 を有効(研究/bench向け
      • C6: 将来的に classes |= 0x40 で C6 を v2 に昇格させる余地を残す。
      • C5: 同様に classes |= 0x20 で C5 を v2 に載せる最終フェーズ。
  • C6 拡張時に検討すべきポイント(設計メモのみ、実装は別フェーズ):
    • ワークロード分布: Mixed 161024B で C6 がどの程度トラフィックを持つか(既存 FRONT_CLASS stats を参考にする)。
    • 以前の Tiny C6 v1 実験では Mixed 回帰があったため、C6 v2 では:
      • Route/Gate で C6 の Hot path を C7 と同じ 1 LUT + 1 switch に揃える。
      • Unified Cache / WarmPool / Superslab へのアクセス回数が増えないようにするpure Hot Box 化を徹底)。
    • rollout 戦略:
      1. C6-only bench プロファイルで C6 v2 を ON にし、C6-heavy なベンチで C6 v1 と A/B少なくとも回帰なし
      2. Mixed 161024B で C7 v2 + C6 v2 構成と C7 v2 のみ構成を比較し、±5% 以内 or 微プラスであれば「C6 v2 を本命候補」とする。
      3. いずれも満たせない場合は、C6 v2 を C7 v2 と同様に研究箱ENV で個別に OFF 可能)として維持し、本線は C7 v2 + C6 v1 のままにする。

メモ:

  • 本ガイドは C7 フェーズまでの実装指針とログを含んでおり、C6/C5 拡張は「同じ TinyHotHeapBox v2 の枠内で class を増やす」作業として扱う。
  • C6/C5 拡張の具体的な API 変更や stats 追加は、今後の Phase (例: Phase62 以降) で別途「C6 v2 指示書」として追記する想定。***