Guard madvise ENOMEM and stabilize pool/tiny front v3
This commit is contained in:
52
docs/design/POOL_HOTBOX_V2_IMPLEMENTATION_GUIDE.md
Normal file
52
docs/design/POOL_HOTBOX_V2_IMPLEMENTATION_GUIDE.md
Normal file
@ -0,0 +1,52 @@
|
||||
# POOL_HOTBOX_V2_IMPLEMENTATION_GUIDE (Phase68 スケルトン)
|
||||
|
||||
目的
|
||||
----
|
||||
- pool allocator のホットパスを TinyHotHeap v2 と同様に Hot/Cold 分離するための実装ガイド。
|
||||
- 現行 v1 をデフォルトのまま残しつつ、ENV ゲートで v2 を A/B できる形で段階的に導入する。
|
||||
|
||||
前提・ゲート
|
||||
------------
|
||||
- デフォルトは v1(`HAKMEM_POOL_V2_ENABLED=0`)。v2 は bench/研究用に opt-in。
|
||||
- クラスマスクを追加する場合は `HAKMEM_POOL_V2_CLASSES`(bit per class)を想定。
|
||||
|
||||
実装ステップ
|
||||
------------
|
||||
1. 型整備(Hot Box)
|
||||
- `pool_page_v2`(freelist/used/capacity/base/meta/token)
|
||||
- `pool_class_v2`(current_page/partial_pages/full_pages/stride)
|
||||
- `pool_ctx_v2`(全クラス配列)
|
||||
|
||||
2. TLS 初期化
|
||||
- `pool_v2_tls_get()` を追加し、calloc + memset で ctx を用意。
|
||||
- 各クラスの block_size/stride を初期化。`max_partial_pages` 等のポリシー値もここで設定。
|
||||
|
||||
3. alloc/free Hot パス
|
||||
- `pool_v2_alloc(class_idx)`:
|
||||
- current → partial → refill の順で freelist pop、page 内 used++。
|
||||
- `pool_v2_free(class_idx, ptr)`:
|
||||
- page_of(ptr) を求め、freelist push / used--。
|
||||
- used==0 なら retire 判定(partial 上限を超えたら Cold へ返却)。
|
||||
- Hot パスでは OS/Stats/学習に触れない。
|
||||
|
||||
4. Cold IF
|
||||
- `PoolColdIface` を定義 (`refill_page`, `retire_page`)。
|
||||
- 初期実装は既存 pool/superslab の refill/retire API をラップして流用し、Hot からは IF だけを見る。
|
||||
|
||||
5. Gate/Route
|
||||
- front 入口は 1 LUT + switch(route)で v1/v2/legacy を選択。
|
||||
- `HAKMEM_POOL_V2_ENABLED` / classes mask で v2 を有効化し、fallback は常に v1 へ。
|
||||
|
||||
6. Stats/Fail-Fast
|
||||
- ENV で有効化できる軽量 stats(alloc_fast/slow/refill/fallback, free_fast/retire 等)を用意。
|
||||
- 不整合(class mismatch/範囲外)は Fail-Fast、フォールバックは明示的にカウントする。
|
||||
|
||||
ベンチ計画(目安)
|
||||
------------------
|
||||
- プロファイル: `./bench_mid_large_mt_hakmem 1 1000000 400 1`(Release, C7_SAFE, v2 OFF が基準)。
|
||||
- 目標: 28–29M ops/s → 30–32M (+5〜10%)。perf stat で cycles を明確に下げる。
|
||||
|
||||
メモ
|
||||
----
|
||||
- Superslab/OS/Stats/Learning のロジックは変えない。Cold IF を挟んで境界を明確にするだけ。
|
||||
- C6-heavy など特定クラスから段階的に v2 を試し、回帰が出たら即ゲートで戻せる構造を優先する。***
|
||||
171
docs/design/SMALLOBJECT_HOTBOX_V3_IMPLEMENTATION_GUIDE.md
Normal file
171
docs/design/SMALLOBJECT_HOTBOX_V3_IMPLEMENTATION_GUIDE.md
Normal file
@ -0,0 +1,171 @@
|
||||
# SmallObjectHotBox v3 Implementation Guide
|
||||
|
||||
対象と前提
|
||||
----------
|
||||
- 対象: Tiny (16〜1KiB) と mid/smallmid の一部を統合して扱う SmallObjectHotBox v3 の実装タスク。
|
||||
- 前提:
|
||||
- 現状の v1 TinyHeap / C7 SAFE / pool v1 を「安定線」とし、v3 は bench/研究プロファイルから段階的に導入する。
|
||||
- TinyHotHeap v2 / pool v2 は研究箱として凍結済み。v3 では設計のインスピレーションとして扱い、直接の継承は行わない。
|
||||
|
||||
0. ゴールと非ゴール
|
||||
-------------------
|
||||
- ゴール:
|
||||
- SmallObjectHotBox v3 を per-thread Hot Box として実装し、C7-only → C6/C5 → mid/smallmid の順に対象クラスを広げられる骨格を作る。
|
||||
- Hot Box 側は heap→page→block のみを扱い、Superslab/Segment/Tier/Guard/Remote との境界を
|
||||
- alloc 側: `so_alloc_refill_slow(...)`
|
||||
- free 側: `so_page_retire_slow(...)`
|
||||
の 2 箇所に集約する。
|
||||
- A/B: PolicySnapshot + ENV (`HAKMEM_SMALL_HEAP_V3_ENABLED`, `HAKMEM_SMALL_HEAP_V3_CLASSES`) で v1/v3 を即切替可能にする。
|
||||
- 非ゴール:
|
||||
- Superslab/Segment/Tier/Guard/Remote の設計変更(サイズや Tier ポリシーの再定義)は行わない。
|
||||
- C0〜C4 や 2KiB 超〜large サイズ帯を v3 ですべて置き換えること(v3 初期フェーズでは 16〜1024B/2048B 付近に絞る)。
|
||||
- 学習層 (ACE/ELO) の仕様変更。PolicySnapshot の更新だけを学習側が持ち、Hot パスは snapshot を読むだけにする。
|
||||
|
||||
1. 型の導入 (Hot Box)
|
||||
---------------------
|
||||
|
||||
ファイル案:
|
||||
- `core/box/smallobject_hotbox_v3_box.h` … 型と inline API
|
||||
- `core/smallobject_hotbox_v3.c` … slow path 実装(任意)
|
||||
|
||||
作業指示:
|
||||
1. `so_page_t` / `so_class_t` / `so_ctx_t` を定義する(`SMALLOBJECT_HOTBOX_V3_DESIGN.md` に準拠)。
|
||||
- `so_page_t` : freelist / used / capacity / class_idx / flags / block_size / base / slab_ref / next。
|
||||
- `so_class_t`: current / partial / max_partial / partial_count / block_size。
|
||||
- `so_ctx_t` : `so_class_t cls[SMALLOBJECT_NUM_CLASSES];`。
|
||||
2. TLS getter `so_tls_get()` を実装:
|
||||
|
||||
```c
|
||||
so_ctx_t* so_tls_get(void) {
|
||||
static __thread so_ctx_t* s_ctx;
|
||||
so_ctx_t* ctx = s_ctx;
|
||||
if (!ctx) {
|
||||
ctx = calloc(1, sizeof(so_ctx_t));
|
||||
if (!ctx) abort();
|
||||
s_ctx = ctx;
|
||||
for (int i = 0; i < SMALLOBJECT_NUM_CLASSES; i++) {
|
||||
ctx->cls[i].block_size = smallobject_block_size_for_class(i);
|
||||
ctx->cls[i].max_partial = default_partial_for_class(i); // 例: 2
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
```
|
||||
|
||||
2. Cold IF (SmallObjectColdIface) の実装
|
||||
---------------------------------------
|
||||
|
||||
ファイル案:
|
||||
- `core/box/smallobject_cold_iface_v1.h`
|
||||
|
||||
作業指示:
|
||||
1. Cold IF struct を定義:
|
||||
|
||||
```c
|
||||
typedef struct SmallObjectColdIface {
|
||||
so_page_t* (*refill_page)(void* cold_ctx, uint32_t class_idx);
|
||||
void (*retire_page)(void* cold_ctx, uint32_t class_idx, so_page_t* page);
|
||||
} SmallObjectColdIface;
|
||||
```
|
||||
|
||||
2. v1 TinyHeap / pool v1 / Superslab の既存 API を呼ぶ v1 ラッパを追加:
|
||||
- `smallobject_cold_refill_page_v1(void* cold_ctx, uint32_t ci)`:
|
||||
- Tiny/Pool v1 側から SmallObject 用ページを 1 枚借りるヘルパ(class_idx→Superslab/Segment→ページ構造を構築)。
|
||||
- `so_page_t` に `base/block_size/capacity/slab_ref` をセットし返す。
|
||||
- `smallobject_cold_retire_page_v1(void* cold_ctx, uint32_t ci, so_page_t* page)`:
|
||||
- page->slab_ref/base を使って v1 側にページ返却。
|
||||
3. `SmallObjectColdIface smallobject_cold_iface_v1(void)` を実装し、Hot Box 側はこの IF を通してしか Cold Box を触らないようにする。
|
||||
|
||||
3. Hot パス実装 (alloc/free)
|
||||
----------------------------
|
||||
|
||||
3-1. alloc_fast / alloc_refill_slow
|
||||
-----------------------------------
|
||||
|
||||
1. `static inline void* so_alloc_fast(so_ctx_t* ctx, uint32_t ci);`
|
||||
- current / partial から freelist pop。失敗時は `NULL` を返す(slow に任せる)。
|
||||
2. `void* so_alloc(uint32_t ci);`
|
||||
- `ctx = so_tls_get();`
|
||||
- fast で hit すればそのまま返す。
|
||||
- miss のとき:
|
||||
- `SmallObjectColdIface cold = smallobject_cold_iface_v1();`
|
||||
- `so_alloc_refill_slow(ctx, ci, &cold)` を呼ぶ。
|
||||
3. `so_alloc_refill_slow` の中で:
|
||||
- v1 Cold IF から `so_page_t*` を 1 枚借りる。
|
||||
- `so_page_t` 内の freelist が未構築なら block_size/capacity/base から carve。
|
||||
- `hc->current` にセットしてから `so_alloc_fast` を再呼び出し。
|
||||
|
||||
3-2. free_fast / page_retire_slow
|
||||
---------------------------------
|
||||
|
||||
1. `static inline void so_free_fast(so_ctx_t* ctx, uint32_t ci, void* ptr);`
|
||||
- `so_page_t* p = so_page_of(ptr);`(O(1)page_of、ヘッダ+mask 前提)。
|
||||
- freelist push, `--used`。
|
||||
- `used==0` のとき:
|
||||
- `partial_count < max_partial` なら partial リストに温存。
|
||||
- それ以外は `so_page_retire_slow()` に渡す。
|
||||
2. `so_page_retire_slow(so_ctx_t* ctx, uint32_t ci, so_page_t* p, SmallObjectColdIface* cold);`
|
||||
- Hot のリストから unlink。
|
||||
- `cold->retire_page(cold_ctx, ci, p)` で Cold Box に返却。
|
||||
- `so_page_t` ノードは free or reuse。
|
||||
|
||||
4. Front/Gate/Route 統合
|
||||
------------------------
|
||||
|
||||
作業指示:
|
||||
1. Route LUT に SmallObjectHotBox v3 用の route を追加:
|
||||
- 例: `TINY_ROUTE_SMALL_HEAP_V3`。
|
||||
2. Policy/ENV:
|
||||
- `HAKMEM_SMALL_HEAP_V3_ENABLED` / `HAKMEM_SMALL_HEAP_V3_CLASSES` を追加し、class_mask で v3 を有効化。
|
||||
- 初期値は 0(全クラス v1 のまま)。
|
||||
3. Front(malloc/free)で:
|
||||
|
||||
```c
|
||||
if (small_heap_v3_enabled_for_class(class_idx)) {
|
||||
void* p = so_alloc((uint32_t)class_idx);
|
||||
if (p) return p;
|
||||
// v3 alloc 失敗時は静かに v1 へ fallback(stats に記録)
|
||||
}
|
||||
// 既存 Tiny/pool/legacy 経路へ
|
||||
```
|
||||
|
||||
- free も同様に、v3 route のみ `so_free` を呼び、それ以外は現行経路へ。
|
||||
|
||||
5. Stats / Fail-Fast / A/B
|
||||
--------------------------
|
||||
|
||||
5-1. Stats
|
||||
---------
|
||||
|
||||
1. `so_stats_class_t` を定義し、クラス別に:
|
||||
- `alloc_calls`, `alloc_fast`, `alloc_refill`, `alloc_fallback_v1`
|
||||
- `free_calls`, `free_fast`, `free_fallback_v1`
|
||||
を `_Atomic` で持つ。
|
||||
2. ENV `HAKMEM_SMALL_HEAP_V3_STATS=1` で destructor から 1 行ダンプ。
|
||||
|
||||
5-2. Fail-Fast
|
||||
--------------
|
||||
|
||||
1. 範囲外 ptr / misaligned / header_missing など page_of の前提が壊れた場合は、デバッグフェーズでは abort する。
|
||||
2. 構造安定後は、Fail-Fast から「ログ+v1 fallback」に切り替えられるように ifdef/ENV を用意する。
|
||||
|
||||
5-3. A/B
|
||||
--------
|
||||
|
||||
1. C7-only プロファイル:
|
||||
- v3 OFF: 現行 C7 SAFE v1(または C7 v2)を基準にする。
|
||||
- v3 ON : C7 クラスだけ SmallObjectHotBox v3 に経路を切り替え、ops/s と stats を比較。
|
||||
2. Mixed 16–1024B:
|
||||
- C7-only v3 と C7+C6 v3 の A/B を取り、±5% 以内 or プラスであれば次のクラスに広げる。
|
||||
|
||||
6. 段階的 rollout と rollback
|
||||
------------------------------
|
||||
|
||||
作業指示:
|
||||
1. すべての v3 変更は ENV で OFF にできるようにする:
|
||||
- `HAKMEM_SMALL_HEAP_V3_ENABLED=0` で完全に v1/v2 へ戻る。
|
||||
2. クラス単位で v3 を有効にする:
|
||||
- まず C7-only(bit7)→ C6/C5 → mid/smallmid の順で classes マスクを広げる。
|
||||
3. どのフェーズでも「v3 を OFF にすれば元の挙動に戻る」ことを前提に、Fail-Fast と stats を優先的に実装する。
|
||||
|
||||
このガイドに従って実装を進めれば、SmallObjectHotBox v3 を Box Theory に沿って段階的に導入しつつ、常に v1/v2 への安全な戻り道を確保できます。実装の各ステップは小さく分け、必ずベンチと stats で A/B を取りながら前に進めてください。***
|
||||
92
docs/design/TINY_FRONT_V3_FLATTENING_GUIDE.md
Normal file
92
docs/design/TINY_FRONT_V3_FLATTENING_GUIDE.md
Normal file
@ -0,0 +1,92 @@
|
||||
# TINY FRONT v3 FLATTENING GUIDE
|
||||
|
||||
Mixed 16–1024B で C7 v3 を ON にしたときの前段ホットパスを薄くするための設計メモ。実装は別フェーズ担当 AI 向けの TODO リストです。
|
||||
|
||||
## 現状の malloc_tiny_fast 前段フロー(ざっくり)
|
||||
- size → 「Tiny かどうか」判定。
|
||||
- Tiny なら size → class_idx (C0〜C7) 変換。
|
||||
- route LUT (`tiny_route_for_class(class_idx)`) で route 決定。
|
||||
- route switch で Tiny v1 / v2 / v3 / legacy を呼び分け。
|
||||
- C7 v3 は早期パスで direct に `so_alloc` / `so_free` へ行くが、他クラスの前段 if/switch がまだ残っている。
|
||||
|
||||
## perf で見えたボトルネック(Mixed 16–1024B, v3 ON, DEBUGビルド)
|
||||
- `tiny_region_id_write_header` ~6.7%
|
||||
- `ss_map_lookup` ~3.6%
|
||||
- `unified_cache_enabled` ~2.8%, `tiny_guard_is_enabled` ~2.2%
|
||||
- `classify_ptr` (size→class 判定) ~1.4%
|
||||
- `mid_desc_lookup` ~1.3%
|
||||
- so_alloc/so_free 自体は合計 ~7%(C7 v3 本体)。ここでは C7 以外の前段を削るのが目的。
|
||||
|
||||
## フラット化方針(次フェーズで実装する項目)
|
||||
1) **size→class→route を 1 LUT + 1 switch に寄せる**
|
||||
- C7 v3 の早期 if を最小限にし、C5/C6 などの分岐を共通 switch にまとめる。
|
||||
- route の再判定・重複チェックを減らす。デバッグ/稀パスは `unlikely` 側へ。
|
||||
|
||||
2) **header/guard 系の前段整理**
|
||||
- `tiny_region_id_write_header` の呼び出し回数・書き込みバイト数を確認し、必要最小限にする。
|
||||
- guard 判定 (`tiny_guard_is_enabled` など) を Snapshot 側に寄せ、front では値を読むだけにする。
|
||||
|
||||
3) **Superslab 判定の軽量化**
|
||||
- C7 v3 で self-thread が確定している場合の `ss_map_lookup` を避けられるか検討。
|
||||
- lookup が必要なケースを TinyLookupBox に閉じ込め、ホットパスは lookup なしで通す。
|
||||
|
||||
## A/B 用プロファイル(固定)
|
||||
- `HAKMEM_BENCH_MIN_SIZE=16`
|
||||
- `HAKMEM_BENCH_MAX_SIZE=1024`
|
||||
- `HAKMEM_TINY_HEAP_PROFILE=C7_SAFE`
|
||||
- `HAKMEM_TINY_C7_HOT=1`
|
||||
- `HAKMEM_TINY_HOTHEAP_V2=0`
|
||||
- `HAKMEM_POOL_V2_ENABLED=0`
|
||||
- `HAKMEM_SMALL_HEAP_V3_ENABLED=1`
|
||||
- `HAKMEM_SMALL_HEAP_V3_CLASSES=0x80`
|
||||
- ベンチ: `./bench_random_mixed_hakmem 1000000 400 1`
|
||||
|
||||
成功ライン: 上記環境で v3 ON のまま **+5〜10%** か、少なくとも回帰なし& HEAP_STATS に異常なし。
|
||||
|
||||
## Phase1 (done): フラグ判定のスナップショット化
|
||||
- ENV ゲート:
|
||||
- `HAKMEM_TINY_FRONT_V3_ENABLED` … デフォルト OFF(有効時のみ v3 フロント経路)
|
||||
- `HAKMEM_TINY_FRONT_V3_STATS` … 任意のデバッグカウンタ用
|
||||
- Snapshot(`TinyFrontV3Snapshot`)に
|
||||
- unified_cache_on
|
||||
- tiny_guard_on
|
||||
- header_mode
|
||||
を 1 回だけキャッシュし、front v3 ON のときは `unified_cache_enabled` / `tiny_guard_is_enabled` / `tiny_header_mode` の呼び出しをホットパスから排除。
|
||||
- malloc/free とも snapshot を読むブロックに入り、UC/guard 判定をキャッシュ経由に統一。
|
||||
- A/B(Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF)で挙動変化なし(slow=1 維持)。次は size→class 前段のフラット化に進める。
|
||||
|
||||
## Phase2-A: size→class LUT 化(実装済み、A/B 待ち)
|
||||
- ENV: `HAKMEM_TINY_FRONT_V3_LUT_ENABLED`(デフォルト OFF、v3 ON 時のみ有効化可能)。
|
||||
- Tiny 前段専用の LUT (`TinyFrontV3SizeClassEntry`) を 1 回だけ構築し、front v3 が有効かつ LUT ON のときは
|
||||
`malloc_tiny_fast` が size→class→route を 1 ルックアップで取得(挙動は既存 `hak_tiny_size_to_class` / route スナップショットに一致)。
|
||||
- フォールバック: LUT が無効/範囲外/未初期化のときは従来の size→class ロジックに自動で戻る。
|
||||
- Mixed 16–1024B A/B(ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF, front v3 ON)
|
||||
- LUT=0: 44.820M ops/s
|
||||
- LUT=1: 45.231M ops/s(+0.9%)
|
||||
- C7_PAGE_STATS: prepare_calls=2446(短尺時と同値)、HEAP_STATS は TinyHeap v1 経路外のため出力なし。
|
||||
- 挙動回帰なしで微プラス。次は size→class→route 前段のさらなるフラット化を検討。
|
||||
|
||||
## Phase2-B: route fast path(LUT→1 switch)
|
||||
- ENV: `HAKMEM_TINY_FRONT_V3_ROUTE_FAST_ENABLED`(デフォルト OFF、front v3 + LUT ON 時だけ opt-in)。
|
||||
- 仕様:
|
||||
- size→class→route を LUT 2 バイト load で取得し、即 1 switch で v3/v2/v1/legacy に分岐。
|
||||
- route helper(tiny_route_for_class)呼び出しをホットパスから外し、挙動は LUT 構築時のスナップショットに従う。
|
||||
- ROUTE_FAST=0 のときは Phase2-A と同じ挙動に自動フォールバック。
|
||||
- A/B(Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF, front v3 ON, LUT ON)
|
||||
- route_fast=0: 45.066M ops/s
|
||||
- route_fast=1: 44.821M ops/s(約 -0.5%)
|
||||
- C7_PAGE_STATS: prepare_calls=2446、回帰なし。HEAP_STATS は TinyHeap v1 経路外のため未出力。
|
||||
- メモ: 微小マイナスだったためデフォルトは ROUTE_FAST=0 のまま。size→class 前段や header/guard 整理の方が効果が出そう。
|
||||
|
||||
## Phase2-C: Header v3 (C7-only skip) 実験
|
||||
- ENV:
|
||||
- `HAKMEM_TINY_HEADER_V3_ENABLED`(デフォルト 0)
|
||||
- `HAKMEM_TINY_HEADER_V3_SKIP_C7`(デフォルト 0、C7 v3 alloc 時に tiny_region_id_write_header をスキップして簡易 1byte だけ書く)
|
||||
- 実装ポイント:
|
||||
- TinyFrontV3Snapshot に header_v3_{enabled,skip_c7} を追加。
|
||||
- so_alloc_fast(C7) で header_v3_skip_c7 が有効なときは header を簡易 1store のみで書き、ガード/重い処理を省略。
|
||||
- front/free 側の挙動は不変(ヘッダは class 判定用に 1byte だけ残す)。
|
||||
- A/B(Mixed 16–1024B, ws=400, iters=1M, front v3/LUT ON, route_fast=0, C7 v3 ON)
|
||||
- header_v3=0: 44.29M ops/s, C7_PAGE_STATS prepare_calls=2446
|
||||
- header_v3=1 + SKIP_C7=1: 43.68M ops/s(約 -1.4%), prepare_calls=2446, v3 fallback/page_of_fail=0
|
||||
- 所感: 短尺の header スキップだけでは改善なし。free 側の header 依存を外す or header_light 再設計を別フェーズで検討。
|
||||
@ -9,6 +9,23 @@
|
||||
- `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 16–1024B の長尺プロファイル(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=0`(C7 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_pages`(C7 デフォルト 2)と `partial_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. ゴールと非ゴール
|
||||
@ -396,3 +413,181 @@ Step 7-6: 再ベンチと判定基準
|
||||
次フェーズで C6/C5 への拡張や C7 超ホットレーン(B案)を検討できる。
|
||||
- 満たせない場合は v2 を引き続き **デフォルト OFF / 研究用箱** としたまま、
|
||||
current_page / retire ポリシーの再修正に戻る。
|
||||
|
||||
---
|
||||
|
||||
## 8. Phase58: TinyColdIface 導入後の課題整理と次の実装指示
|
||||
|
||||
現状ステータス(Phase58 時点)
|
||||
------------------------------
|
||||
- `core/box/tiny_cold_iface_v1.h` に `TinyColdIface` が導入され、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_v2` を `calloc` し、各クラスの `storage_page` を reset、`stride` を `tiny_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_page` を `ipage` として取り出し、
|
||||
- `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:
|
||||
|
||||
```c
|
||||
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_page` は **Cold 側に返すための 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 fallback(C7-only 実験箱のまま)。
|
||||
- `v2ctx == NULL` / `hc == NULL` / `hc->stride == 0` などは Fail-Fast(abort)で早期検出。
|
||||
- `tiny_hotheap_v2_try_pop()`:
|
||||
- `candidate->capacity == 0` や `candidate->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 16–1024B:
|
||||
- 短尺 (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 16–1024B 全体の性能を押し上げる。
|
||||
- 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 16–1024B で 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 16–1024B で 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 指示書」として追記する想定。***
|
||||
|
||||
Reference in New Issue
Block a user