# Tiny Learning Layer & Backend Integration (Phase 27 Snapshot) > **⚠️ ARCHIVE NOTICE (2025-11-26)** > Several ENV variables and files referenced in this document were removed in commit 6b791b97d: > - `HAKMEM_TINY_ULTRA_FRONT` (deleted) > - `HAKMEM_TINY_ULTRA_L0` (deleted) > - `HAKMEM_TINY_ULTRA_HEAP_DUMP` (deleted) > - `HAKMEM_TINY_ULTRA_PAGE_DUMP` (deleted) > - `HAKMEM_TINY_BG_REMOTE` (deleted) > - `HAKMEM_TINY_BG_REMOTE_BATCH` (deleted) > - Files deleted: `core/front/tiny_ultrafront.h`, `core/ultra/tiny_ultra_heap.c`, `core/ultra/tiny_ultra_page_arena.c` > > This document is kept for historical reference but may contain outdated information. **Date**: 2025-11-21 **Scope**: Tiny (0–1KB) / Shared Superslab Pool / FrozenPolicy / Ultra* Boxes **Goal**: 学習層(FrozenPolicy / Learner)を活かして、Tiny の backend を「自動でそれなりに最適」な状態に保つための箱と境界を整理する。 --- ## 1. Box Topology(Tiny 向けの学習レイヤ構成) - **Box SP-SLOT (SharedSuperSlabPool)** - ファイル: `core/hakmem_shared_pool.{h,c}`, `core/superslab/superslab_types.h` - 役割: - Tiny クラス 0..7 向けの Superslab を **共有プール**として管理(per-class SuperSlabHead legacy を徐々に退役)。 - Slot state: `SLOT_UNUSED / SLOT_ACTIVE / SLOT_EMPTY` を per-slab で追跡。 - 主要フィールド: - `_Atomic uint64_t g_sp_stage1_hits[cls]` … EMPTY 再利用 (Stage1) - `_Atomic uint64_t g_sp_stage2_hits[cls]` … UNUSED claim (Stage2) - `_Atomic uint64_t g_sp_stage3_hits[cls]` … 新規 SuperSlab (Stage3) - `uint32_t class_active_slots[TINY_NUM_CLASSES_SS]` … クラス別 ACTIVE slot 数 - 主要 API: - `shared_pool_acquire_slab(int class_idx, SuperSlab** ss, int* slab_idx)` - `shared_pool_release_slab(SuperSlab* ss, int slab_idx)` - ENV: - `HAKMEM_SHARED_POOL_STAGE_STATS=1` → プロセス終了時に Stage1/2/3 の breakdown を 1 回だけダンプ。 - **Box TinySuperslab Backend Box (`hak_tiny_alloc_superslab_box`)** - ファイル: `core/hakmem_tiny_superslab.{h,c}` - 役割: - Tiny front(Unified / UltraHeap / TLS)から Superslab backend への **唯一の出入口**。 - shared backend / legacy backend / hint Box を 1 箇所で切り替える。 - Backend 実装: - `hak_tiny_alloc_superslab_backend_shared(int class_idx)` → Shared Pool / SP-SLOT 経由。 - `hak_tiny_alloc_superslab_backend_legacy(int class_idx)` → 旧 `SuperSlabHead` ベース(回帰・fallback 用)。 - `hak_tiny_alloc_superslab_backend_hint(int class_idx)` → legacy に落ちる前に、直近の (ss, slab_idx) を 1 回だけ再利用する軽量 Box。 - ENV: - `HAKMEM_TINY_SS_SHARED=0` → 強制 legacy backend のみ。 - `HAKMEM_TINY_SS_LEGACY_FALLBACK=0` → shared 失敗時にも legacy を使わない(完全 Unified モード)。 - `HAKMEM_TINY_SS_C23_UNIFIED=1` → **C2/C3 だけ legacy fallback を無効化**(他クラスは従来どおり shared+legacy)。 - `HAKMEM_TINY_SS_LEGACY_HINT=1` → shared 失敗 → legacy の間に hint Box を挟む。 - **Box FrozenPolicy / Learner(学習層)** - ファイル: `core/hakmem_policy.{h,c}`, `core/hakmem_learner.c` - 役割: - Mid/Large で実績がある CAP/W_MAX 調整ロジックを Tiny に拡張する足場。 - Tiny 向けフィールド: - `uint16_t tiny_cap[8]; // classes 0..7` → Shared Pool の「クラス別 ACTIVE slot 上限」(soft cap)。 - Tiny CAP デフォルト(Phase 27 時点): - `{2048, 1024, 96, 96, 256, 256, 128, 64}` → C2/C3 は Shared Pool 実験対象として 96/96 に設定。 - ENV: - `HAKMEM_CAP_TINY=2048,1024,96,96,256,256,128,64` → 先頭から 8 個を `tiny_cap[0..7]` に上書き。 - **Box UltraPageArena(Tiny→Page 層の観察箱)** - ファイル: `core/ultra/tiny_ultra_page_arena.{h,c}` - 役割: - `superslab_refill(int class_idx)` をフックし、クラス別の Superslab refill 回数をカウント。 - API: - `tiny_ultra_page_on_refill(int class_idx, SuperSlab* ss)` - `tiny_ultra_page_stats_snapshot(uint64_t refills[8], int reset)` - ENV: - `HAKMEM_TINY_ULTRA_PAGE_DUMP=1` → 終了時に `[ULTRA_PAGE_STATS]` を 1 回だけダンプ。 --- ## 2. 学習ループに見せるメトリクス Tiny 学習層が見るべきメトリクスと取得元: - **Active Slot / CAP 関連** - `g_shared_pool.class_active_slots[class]` → クラス別 ACTIVE slot 数(Shared Pool 管理下)。 - `FrozenPolicy.tiny_cap[class]` → soft cap。`shared_pool_acquire_slab` Stage3 で `cur >= cap` なら **新規 Superslab 拒否**。 - **Acquire Stage 内訳** - `g_sp_stage1_hits[class]` … Stage1 (EMPTY slot 再利用) - `g_sp_stage2_hits[class]` … Stage2 (UNUSED slot claim) - `g_sp_stage3_hits[class]` … Stage3 (新規 SuperSlab / LRU pop) - これらの合算から: - Stage3 割合が高い → Superslab churn が多い、CAP/Precharge/LRU を増やす候補。 - Stage1 が長期間 0% → EMPTY スロットがほぼ生成されていない(free 側のポリシー改善候補)。 - **Page 層イベント** - `TinyUltraPageStats.superslab_refills[cls]` → クラス別の refill 回数。Tiny front から見た「page 層イベントの多さ」を測る。 --- ## 3. 現状のポリシーと挙動(Phase 27) ### 3.1 Shared Pool backend 選択 `hak_tiny_alloc_superslab_box(int class_idx)` のポリシー: 1. `HAKMEM_TINY_SS_SHARED=0` のとき: - 常に legacy backend (`hak_tiny_alloc_superslab_backend_legacy`) のみを使用。 2. shared 有効時: - 基本経路: - `p = hak_tiny_alloc_superslab_backend_shared(class_idx);` - `p != NULL` ならそのまま返す。 - fallback 判定: - `HAKMEM_TINY_SS_LEGACY_FALLBACK=0` → shared 失敗でも legacy へは落とさず、そのまま `NULL` 許容(完全 Unified モード)。 - `HAKMEM_TINY_SS_C23_UNIFIED=1` → C2/C3 の場合に限り `legacy_fallback=0` に上書き(他クラスは `g_ss_legacy_fallback` に従う)。 - hint Box: - shared 失敗 & fallback 許可時に限り: - `hak_tiny_alloc_superslab_backend_hint(class_idx)` を 1 回だけ試す。 - 直近成功した `(ss, slab_idx)` がまだ `used < capacity` なら、そこから 1 ブロックだけ追加 carve。 ### 3.2 FrozenPolicy.tiny_cap と Shared Pool の連携 - `shared_pool_acquire_slab()` Stage3(新規 Superslab 確保)直前に: ```c uint32_t limit = sp_class_active_limit(class_idx); // = tiny_cap[class] uint32_t cur = g_shared_pool.class_active_slots[class_idx]; if (limit > 0 && cur >= limit) { return -1; // Soft cap reached → caller 側で legacy fallback or NULL } ``` - 意味: - `tiny_cap[class]==0` → 制限なし(無限に Superslab を増やせる)。 - `>0` → ACTIVE slot 数が cap に達したら **新規 SuperSlab を増やさない**(churn 制御)。 現状のデフォルト: - `{2048,1024,96,96,256,256,128,64}` - C2/C3 を 96 に抑えつつ、C4/C5 は 256 slots まで許容。 - ENV `HAKMEM_CAP_TINY` で一括上書き可能。 ### 3.3 C2/C3 限定「ほぼ完全 Unified」実験 - `HAKMEM_TINY_SS_C23_UNIFIED=1` のとき: - C2/C3: - shared backend のみで運転(`legacy_fallback=0`)。 - Shared Pool から Superslab/slab が取れなかった場合は `NULL` を返し、上位が UltraFront/TinyFront 経路にフォールバック。 - 他クラス: - 従来どおり shared+legacy fallback。 - Random Mixed 256B / 200K / ws=256 での挙動: - デフォルト設定(C2/C3 cap=96): ≈16.8M ops/s 前後。 - `HAKMEM_TINY_SS_C23_UNIFIED=1` の有無で差は ±数% レベル(ランダム揺らぎ内)。 - OOM / SEGV は観測されず、C2/C3 を Shared Pool 単独で回す足場としては安定。 --- ## 4. 「学習層を活かす」ための次ステップ(Tiny 向け) 今ある土台を使って、学習層を Tiny に伸ばすときの具体的なステップと現状: 1. **Learner に Tiny メトリクスを配線(済)** - `core/hakmem_learner.c` に Tiny 専用メトリクスを追加済み: - `active_slots[class] = g_shared_pool.class_active_slots[class];` - `stage3_ratio[class] = ΔStage3 / (ΔStage1+ΔStage2+ΔStage3);` - `refills[class] = tiny_ultra_page_global_stats_snapshot()` から取得。 2. **tiny_cap[] のヒルクライム調整(実装済み/チューニング中)** - 各 Tiny クラスごとに、ウィンドウ内の Stage3 割合を監視: - Stage3 が多すぎ(新規 SuperSlab が頻発) → `tiny_cap[class]` を +Δ。 - Stage3 が少ない & ACTIVE slot が少ない → `tiny_cap[class]` を -Δ。 - cap の下限は `max(min_tiny, active_slots[class])` にクリップし、 既に確保済みの Superslab を急に「上限超過」にしないようにしている。 - 調整後は `hkm_policy_publish()` で新しい FrozenPolicy を公開。 3. **PageArena / Precharge / Cache との連携(TinyPageAuto, 実験中)** - UltraPageArena / SP-SLOT / PageFaultTelemetry からのメトリクスを使って、Superslab OS キャッシュ+precharge を軽く制御: - `HAKMEM_TINY_PAGE_AUTO=1` のとき、Learner が各ウィンドウで - `refills[class]`(UltraPageArena の Superslab refill 数, C2〜C5)と - PageFaultTelemetry の `PF_pages(C2..C5)` および `PF_pages(SSM)` を読み取り、 - `score = refills * PF_pages(Cn) + PF_pages(SSM)/8` を計算。 - スコアが `HAKMEM_TINY_PAGE_MIN_REFILLS * HAKMEM_TINY_PAGE_PRE_MIN_PAGES` 以上のクラスだけに対して: - `tiny_ss_precharge_set_class_target(class, target)`(既定 target=1)で precharge を有効化。 - `tiny_ss_cache_set_class_cap(class, cap)`(既定 cap=2)で OS Superslab キャッシュ枚数を small cap に設定。 - スコアがしきい値未満のクラスは `target=0, cap=0` に戻して OFF。 - これにより、Tiny 側から見て Superslab 層の「refill + PF が重いクラスだけ少数の Superslab を先行 fault-in / 温存」する挙動を学習層から制御できる状態まで到達している(まだパラメータ調整段階)。 4. **Near-Empty しきい値の学習統合(C2/C3)** - Box: `TinyNearEmptyAdvisor`(`core/box/tiny_near_empty_box.{h,c}`) - free パスで C2/C3 の `TinySlabMeta.used/cap` から「near-empty slab」を検出し、イベント数を集計。 - ENV: - `HAKMEM_TINY_SS_PACK_C23=1` … near-empty 観測 ON。 - `HAKMEM_TINY_NEAREMPTY_PCT=P` … 初期しきい値 (%), 1〜99, 既定 25。 - `HAKMEM_TINY_NEAREMPTY_DUMP=1` … 終了時に `[TINY_NEAR_EMPTY_STATS]` を 1 回ダンプ。 - Learner 側からの自動調整: - `HAKMEM_TINY_NEAREMPTY_AUTO=1` のとき、 - ウィンドウ内で near-empty イベント(C2/C3 合計)が 0 の場合: - しきい値 P を `+STEP` だけ緩める(P_MAX まで、STEP 既定 5)。 - near-empty イベントが多すぎる(例: 128 以上)の場合: - P を `-STEP` だけ締める(P_MIN まで)。 - P_MIN/P_MAX/STEP はそれぞれ - `HAKMEM_TINY_NEAREMPTY_PCT_MIN`(既定 5) - `HAKMEM_TINY_NEAREMPTY_PCT_MAX`(既定 80) - `HAKMEM_TINY_NEAREMPTY_PCT_STEP`(既定 5) で上書き可能。 - Random Mixed / Larson では near-empty イベント自体がほとんど発生しておらず、 現状は P がゆるやかに上限側へ寄るだけ(挙動への影響はごく小さい)。 5. **総合スコアでの最適化** - 1 ベンチ(例: Random Mixed 256B)ではなく: - Fixed-size Tiny - Random Mixed 各サイズ - Larson / Burst / Apps 系 をまとめたスコア(平均 ops/s + メモリフットプリント + page fault)に対して、 - Tiny/Learning 層が CAP/Precharge/Cache を少しずつ動かすイメージ。 --- ## 6. 既知の制限と安全策 - 8192B Random Mixed で発生していた TLS SLL head=0x60 問題は: - `tls_sll_pop()` 内で head が低アドレスの場合に、そのクラスの SLL をリセットし slow path に逃がす形で **箱の内側で Fail-Fast** させるように修正済み。 - これにより、長尺ベンチでも SEGV せずに回し続けられる。 - `tiny_nextptr.h` の `tiny_next_store()` には軽いガードを入れ、 - `next` が 0 以外かつ `<0x1000` / `>0x7fff...` の場合に 1 回だけ `[NEXTPTR_GUARD]` を出すようにしてある(観測専用)。 - 現時点の観測では C4 で一度だけ `next=0x47` が記録されており、freelist/TLS 経路のどこかに残存バグがあることは認識済み。 - ただし Fail-Fast により箱の内側でリセットされるため、外側の挙動(ベンチ・アプリ)は安定している。 将来的に「完全退治」まで進める場合は、Tiny 向け debug ビルド構成を整えたうえで `NEXTPTR_GUARD` の call site を `addr2line` などで特定し、当該経路をピンポイントに修正する予定。