Files
hakmem/AGENTS.md

241 lines
15 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.

# AGENTS: 箱理論Box Theory設計ガイドライン
本リポジトリでは、変更・最適化・デバッグを一貫して「箱理論Box Theory」で進めます。すべてを“箱”で分け、境界で接続し、いつでも戻せるように積み上げることで、複雑性を抑えつつ失敗コストを最小化します。
---
## 何が効くのか(実績)
- ❌ Rust/inkwell: 複雑なライフタイム管理
- ✅ 箱理論適用: 650行 → 100行SSA構築
なぜ効果があるか:
- PHI/Block/Value を「箱」として扱い、境界変換点を1箇所に集約
- 複雑な依存関係を箱の境界で切ることで単体検証が容易
- シンプルな Python/llvmlite で 2000行で完結道具に依存せず“箱”で分割して繋ぐ
補足C 実装時の利点)
- C の場合は `static inline` により箱間のオーバーヘッドをゼロに近づけられる(インライン展開)
---
## 🎯 AI協働での合言葉5原則
1. 「箱にする」: 設定・状態・橋渡しは Box 化
- 例: TLS 状態、SuperSlab adopt、remote queue などは役割ごとに箱を分離
2. 「境界を作る」: 変換は境界1箇所で
- 例: adopt → bind、remote → freelist 統合、owner 移譲などの変換点を関数1箇所に集約
3. 「戻せる」: フラグ・feature で切替可能に
- `#ifdef FEATURE_X` / 環境変数 で新旧経路を A/B 可能に(回帰や切り戻しを即時化)
4. 「見える化」: ダンプ/JSON/DOT で可視化
- 1回だけのワンショットログ、統計カウンタで“芯”を掴む常時ログは避ける
5. 「Fail-Fast」: エラー隠さず即座に失敗
- ENOMEM/整合性違反は早期に露呈させる(安易なフォールバックで隠さない)
要するに: 「すべてを箱で分けて、いつでも戻せるように積み上げる」設計哲学にゃ😺🎁
---
## 適用ガイド(このリポジトリ)
- 小さく積むBox 化)
- Remote Free Queue, Partial SS Adopt, TLS Bind/Unbind を独立した“箱”として定義
- 箱の API は最小・明確init/publish/adopt/drain/bind など)
- 境界は1箇所
- Superslab 再利用の境界は `superslab_refill()` に集約publish/adopt の接点)
- Free の境界は “same-thread / cross-thread” の判定1回
- 切替可能(戻せる)
- 新経路は `#ifdef` / 環境変数でオンオフA/B と回帰容易化)
- 例: `HAKMEM_TINY_PHASE6_ULTRA_SIMPLE``HAKMEM_DEBUG_VERBOSE``HAKMEM_TINY_*` env
- 見える化(最小限)
- 1回だけのデバッグ出力ワンショットと統計カウンタで芯を掴む
- 例: [SS OOM]、[SS REFILL] のワンショットログ、alloc/freed/bytes の瞬間値
- Fail-Fast
- ENOMEM・整合性違反はマスクせず露出。フォールバックは“停止しないための最後の手段”に限定
- 運用ルールPool flatten
- Pool v1 flatten / Zero Mode は LEGACY mid/smallmid ベンチ専用の箱。C7_SAFE プロファイルでは flatten を触らない(安定性優先のため常時 OFF
---
## 実装規約C向けの具体
- `static inline` を多用し箱間の呼び出しをゼロコスト化
- 共有状態は `_Atomic` で明示、CAS ループは局所化MPSC push/pop はユーティリティ化)
- 競合制御は「箱の内側」に閉じ込め、外側はシンプルに保つ
- 1つの箱に 1つの責務publish/adopt、drain、bind、owner 移譲 など)
---
## チェックリストPR/レビュー時)
- 箱の境界は明確か変換点が1箇所に集約されているか
- フラグで戻せるかA/B が即時に可能か)
- 可視化のフックは最小か(ワンショット or カウンタ)
- Fail-Fast になっているか(誤魔化しのフォールバックを入れていないか)
- C では `static inline` でオーバーヘッドを消しているか
---
この AGENTS.md は、箱理論の適用・コーディング・デバッグ・A/B 評価の“共通言語”です。新しい最適化や経路を足す前に、まず箱と境界を設計してから手を動かしましょう。
---
## Learning LayerACE / ELO / Learnerの箱化ポリシー
ACE/ELO/CAP Learner などの「学習する機能」も、Tiny や Superslab と同様に Box として扱います。
ホットパスを汚さず、いつでも FROZEN/OFF に戻せるようにすることが原則です。
- 学習は上層の箱に閉じ込める
- L0 Tiny / L1 Superslab / TLS SLL / Remote Queue は **学習ロジックを持たない**
- ELO / ACE Controller / CAP Learner は「ポリシー箱」として、ノブやしきい値だけを更新する。
- ホットパス側は「現在のポリシー値」を読むだけにし、学習の有無を意識しない。
- FROZEN / LEARN / OBSERVE を明確に分ける
- FROZEN: 学習 OFF。固定ポリシーのみ使用デフォルト運用
- LEARN: バックグラウンドでポリシー更新を行う(ベンチ / 研究用)。
- OBSERVE: Tiny などの下層の箱を「観測だけ」するモード(動作は変えない)。
- モード切り替えは `HAKMEM_MODE` / `HAKMEM_ACE_ENABLED` / `HAKMEM_LEARN` 等の ENV から行い、いつでも戻せるようにする。
- ドキュメントと設計の窓口
- 学習レイヤ全体の構造と責務は `docs/analysis/LEARNING_LAYER_OVERVIEW.md` にまとめる。
- 各学習 BoxELO / ACE Controller / CAP Learnerの詳細仕様や A/B 結果は、docs/analysis / docs/benchmarks / docs/paper 配下に箱ごとに整理する。
- AGENTS.md では「学習は必ず別箱」「ホットパスを汚さない」「ENV で切り戻せる」というルールだけを守る。
---
## Tiny 向け「積み方 v2」(層を下から固める)
下層の箱が壊れている状態で上層を積むと必ず崩れます。まず下から順に箱を“堅牢化”してから上を載せる、を徹底します。
層と責務
- Box 1: Atomic Ops (最下層)
- 役割: `stdatomic.h` による CAS/Exchange の秩序付けAcquire/Release
- ルール: メモリ順序を箱内で完結させる(外側に弱い順序を漏らさない)。
- Box 2: Remote Queue (下層)
- 役割: cross-thread free の MPSC スタックpush/exchangeとカウント管理。
- API: `ss_remote_push(ss, slab_idx, ptr) -> transitioned(0/1)``ss_remote_drain_to_freelist(ss, slab_idx)``ss_remote_drain_light(ss)`
- 不変条件 (Invariants):
- push はノードの next を書き換える以外に副作用を持たないfreelist/owner へは触れない)。
- head は SuperSlab 範囲内Fail-Fast 範囲検証)。
- `remote_counts[s]` は push/drain で単調に整合するdrain 後は 0
- 境界: freelist への統合は必ず drain 関数内1 箇所。publish/adopt からの直接 drain は禁止。
- Box 3: Ownership (中層)
- 役割: slab の所有者遷移(`owner_tid`)。
- API: `ss_owner_try_acquire(meta, tid) -> bool``owner_tid==0` の時のみ CAS で取得)、`ss_owner_release(meta, tid)``ss_owner_is_mine(meta, tid)`
- 不変条件:
- Remote Queue は owner に触らないBox 2→3 への侵入禁止)。
- Acquire 成功後のみ “同一スレッド” の高速経路を使用する。
- 境界: bind 時にのみ acquire/release を行う(採用境界 1 箇所)。
- Box 4: Publish / Adopt (上層)
- 役割: 供給の提示publishと消費adopt
- API: `tiny_publish_notify(class, ss, slab)``tiny_mailbox_publish()``tiny_mailbox_fetch()``ss_partial_publish()``ss_partial_adopt()`
- 不変条件:
- publish は “通知とヒント” のみfreelist/remote/owner に触れない)。
- `ss_partial_publish()` は unsafe drain をしない。必要なら drain は採用側境界で実施。
- publish 時に `owner_tid=0` を設定してもよいが、実際の acquire は採用境界でのみ行う。
- 境界: adopt 成功直後にだけ `drain → bind → owner_acquire` を行う(順序は必ずこの 1 箇所)。
実装ガイド(境界の 1 か所化)
- Refill 経路(`superslab_refill()` / `tiny_refill_try_fast()`)でのみ:
1) sticky/hot/bench/mailbox/reg を “peek して” 候補を得る
2) 候補が見つかったら当該 slab で `ss_remote_drain_to_freelist()` を 1 回だけ実行(必要時)
3) freelist が非空であれば `tiny_tls_bind_slab()``ss_owner_try_acquire()` の順で確定
4) 確定後にのみ publish/overflow は扱う(不要な再 publish/drain はしない)
Do / Dont壊れやすいパターンの禁止
- Dont: Remote Queue から publish を直接呼ばない条件分岐を増やす(通知の濫用)。
- Dont: publish 側で drain / owner をいじる。
- Do: Remote Queue は push と count 更新のみ。publish は通知のみ。採用境界で drain/bind/owner を一度に行う。
デバッグ・トリアージ順序FailFast
1) Box 2Remote単体: push→drain→freelist の整合をアサート(範囲検証 ON, `remote_counts` 符合)。
2) Box 3Ownership単体: `owner_tid==0` からの acquire/release を並行で連続試験。
3) Box 4Publish/Adopt単体: publish→mailbox_register/fetch の通電fetch ヒット時のみ adopt を許可)。
4) 全体: adopt 境界でのみ `drain→bind→owner_acquire` を踏んでいるかリングで確認。
可視化と安全化(最小構成)
- Tiny Ring: `TINY_RING_EVENT_REMOTE_PUSH/REMOTE_DRAIN/MAILBOX_PUBLISH/MAILBOX_FETCH/BIND` を採用境界前後に記録。
- EnvA/B・切戻し:
- `HAKMEM_TINY_SS_ADOPT=1/0`publish/adopt 全体の ON/OFF
- `HAKMEM_TINY_RF_FORCE_NOTIFY=1`(初回通知の見逃し検出)
- `HAKMEM_TINY_MAILBOX_SLOWDISC(_PERIOD)`(遅延登録の発見)
- `HAKMEM_TINY_MUST_ADOPT=1`mmap 直前の採用ゲート)
最小テスト(箱単位の smoke
- Remote Queue: 同一 slab へ N 回 `ss_remote_push()``ss_remote_drain_to_freelist()``remote_counts==0` と freelist 長の一致。
- Ownership: 複数スレッドで `ss_owner_try_acquire()` の成功が 1 本だけになること、`release` 後に再取得可能。
- Publish/Mailbox: `tiny_mailbox_publish()``tiny_mailbox_fetch()` のヒットを 1 回保証。`fetch_null` のとき `used` 拡張が有効。
運用の心得
- 下層Remote/Ownershipに疑義がある間は、上層Publish/Adoptを “無理に” 積み増さない。
- 変更は常に A/B ガード付きで導入し、SIGUSR2/リングとワンショットログで芯を掴んでから上に進む。
---
## 健康診断ランと注意事項Superslab / madvise / Pool 用)
このリポジトリは Superslab / madvise / Pool v1 flatten など OS 依存の経路を多用します。
「いつの間にか壊れていた」を防ぐために、次の“健康診断ラン”と注意事項を守ってください。
※ 開発マシンと OS 環境は、基本的にセッションを通じて同一構成(同一ハードウェア・同一カーネル設定)を維持して運用しています。性能の変化は「コード差分」か「明示的に変更した ENV」の影響として扱い、マシン側の揺らぎで説明しない方針です。
- DSO 領域には触らないSuperslab OS Box のフェンス)
- `core/box/ss_os_acquire_box.h``ss_os_madvise_guarded()`**libc/libm/ld.so など DSO 領域を dladdr で検出したら即スキップ** します。
- DSO に対する madvise 試行は **バグ扱い**`g_ss_madvise_disabled` / DSO-skip ログを必ず 1 回だけ出し、以降は触らない前提です。
- 開発/CI では(必要なら)`HAKMEM_SS_MADVISE_DSO_FAILFAST=1` を使って、「DSO に一度でも触ろうとしたら即 abort」するチェックランを追加してください。
- madvise / vm.max_map_count 用 健康診断ラン
- 目的: Superslab OS Box が ENOMEM(vm.max_map_count) に達しても安全に退避できているか、DSO 領域を誤って触っていないかを確認する。
- 推奨コマンドC7_SAFE + mid/smallmid, Superslab/madvise 経路の smoke 用):
```sh
HAKMEM_BENCH_MIN_SIZE=257 \
HAKMEM_BENCH_MAX_SIZE=768 \
HAKMEM_TINY_HEAP_PROFILE=C7_SAFE \
HAKMEM_TINY_C7_HOT=1 \
HAKMEM_TINY_HOTHEAP_V2=0 \
HAKMEM_SMALL_HEAP_V3_ENABLED=1 \
HAKMEM_SMALL_HEAP_V3_CLASSES=0x80 \
HAKMEM_POOL_V2_ENABLED=0 \
HAKMEM_POOL_V1_FLATTEN_ENABLED=0 \
HAKMEM_SS_OS_STATS=1 \
./bench_mid_large_mt_hakmem 5000 256 1
```
- チェックポイント:
- 終了時に `[SS_OS_STATS] ... madvise_enomem=0 madvise_disabled=0` が理想(環境次第で ENOMEM は許容、ただし disabled=1 になっていれば以降の madvise は止まっている)。
- DSO-skip や DSO Fail-Fast ログが出ていないこと(出た場合は ptr 分類/経路を優先的にトリアージ)。
- Pool v1 flatten のプロファイル注意
- LEGACY プロファイル専用の最適化です。`HAKMEM_TINY_HEAP_PROFILE=C7_SAFE` / `C7_ULTRA_BENCH` のときは **コード側で強制OFF** されます。
- flatten を触るときの健康診断ランLEGACY想定:
```sh
HAKMEM_BENCH_MIN_SIZE=257 \
HAKMEM_BENCH_MAX_SIZE=768 \
HAKMEM_TINY_HEAP_PROFILE=LEGACY \
HAKMEM_POOL_V2_ENABLED=0 \
HAKMEM_POOL_V1_FLATTEN_ENABLED=1 \
HAKMEM_POOL_V1_FLATTEN_STATS=1 \
./bench_mid_large_mt_hakmem 1 1000000 400 1
```
- チェックポイント:
- `[POOL_V1_FLAT] alloc_tls_hit` / `free_tls_hit` が増えていることflatten 経路が効いている)。
- `free_fb_*`page_null / not_mine / otherは**少数**に収まっていること。増えてきたら owner 判定/lookup 側を優先トリアージする。
- 一般ルール(壊れたらまず健康診断ラン)
- Tiny / Superslab / Pool に手を入れたあと、まず上記の健康診断ランを 1 回だけ回してから長尺ベンチ・本番 A/B に進んでください。
- 健康診断ランが落ちる場合は **新しい最適化を積む前に** Box 境界ptr 分類 / Superslab OS Box / Pool v1 flatten Boxを優先的に直します。
- ベンチや評価を始めるときは、`docs/analysis/ENV_PROFILE_PRESETS.md` のプリセットMIXED_TINYV3_C7_SAFE / C6_HEAVY_LEGACY_POOLV1 / DEBUG_TINY_FRONT_PERFから必ずスタートし、追加した ENV はメモを残してください。単発の ENV を散らすと再現が難しくなります。
- 健康診断ラン(簡易チェック)は次の 2 本を必ず通す:
- Tiny/Mixed: `HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE ./bench_random_mixed_hakmem 1000000 400 1` 35〜38M ops/s / segv/assert なしで OK
- mid/smallmid C6: `HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1 ./bench_mid_large_mt_hakmem 1 1000000 400 1` 約29M ops/s / segv/assert なしで OK
- まとめて実行する場合は `scripts/verify_health_profiles.sh` を 1 回叩けば OKスループットは目安表示のみ、exit code だけで判定)。