# 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 Layer(ACE / 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` にまとめる。 - 各学習 Box(ELO / 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 / Don’t(壊れやすいパターンの禁止) - Don’t: Remote Queue から publish を直接呼ばない条件分岐を増やす(通知の濫用)。 - Don’t: publish 側で drain / owner をいじる。 - Do: Remote Queue は push と count 更新のみ。publish は通知のみ。採用境界で drain/bind/owner を一度に行う。 デバッグ・トリアージ順序(Fail‑Fast) 1) Box 2(Remote)単体: push→drain→freelist の整合をアサート(範囲検証 ON, `remote_counts` 符合)。 2) Box 3(Ownership)単体: `owner_tid==0` からの acquire/release を並行で連続試験。 3) Box 4(Publish/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` を採用境界前後に記録。 - Env(A/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 だけで判定)。