11 KiB
11 KiB
P28-02「意味を保つ軽量化」仕様(候補 r02。未登録・未実行)
- 状態: 候補です。Root が原本の条件と照合して main に commit し、その実 SHA を base にしてから、通常の登録をします。
- 前提: P28-01 は正式に完了しています(Q
ae9ae47646434dd57fc21f3e8566d0d35b242ab1、execution/p28-01/P28_01_FORMAL_COMPLETION_AE9AE47.jsonf778ac8d…)。その測定(full9db9d613、r02、RESTART)と、製品 source 1407 を引き継ぎます。
1. 原本の条件(変更しない)
- 成果単位 P28-02(
work_packages.jsonの P28.remaining_deliverables)- work: 失敗時拒否を含む同等性を先に固定し、snapshot変換・検証・コピー・経路接続の実測上位から改善する。
- acceptance: 同条件の反復比較と全状態・全イベント一致を確認し、キャッシュの古さ・所有権・不正入力を検証する。
- P28 の横断条件6件(原文): 原本条件とUI操作が一致 / Runtimeの実利用・会計・行動へ接続 / 途中保存・再起動で再現 / 異常/境界/中断を検証 / 関連回帰PASS / 要件別ログとソースハッシュを保存。
- パッケージの依存: P28 は P27 に依存し、P27 は in_progress です。この依存は、P28 パッケージを最終的に完了と判定するときの前提です。P28-02 の診断と作業を止める理由にはしません(既存の、人間による順序変更の許可を保持します)。P27 が完了したとは主張しません。
- 分母は 30/88 で、部分点はありません。現在の正式完了は 23/88、パッケージは 4/30 です。予算の false 6件(R0 の2件、P1 の2件、P2 の2件)は、そのまま保持します。
2. 事実と不足(数値を結びつけない)
- 実測(P28-01 から引き継ぐ。それぞれの区間の値で、互いに結びつけない):
- full9 の R: kernel の
process_real_deltaの1回の呼出し(frame)が、P1 x1 で平均 479.210 ms、P2 x3 で P95 5062.697 ms。 - r02(headless・Ops OFF・x1。別の区間): 外側の1分 advance が平均 826.6 ms、外側の snapshot が 77.6 ms(count 5)、保存は約 300 ms、読込は約 3.2 s。
- full9 の R: kernel の
- source から確定していること: 毎 tick の PUBLISH_SNAPSHOT で
create_snapshot()が呼ばれます(autoload/simulation_runtime.gd:12616-12621)。ただし、r02 の 77.6 ms を、P1 の 479 ms の中の同じ呼出しの固定の費用として扱うことはしません(区間も条件も違うため)。 - 不足: 現在の source での、tick の中の phase ごとの wall 区間、snapshot・検証・コピーの内訳、経路の費用。推測で埋めません。
3. 段階A: P28-02-DIAG(診断。製品は変更しない。原本の加算は0)
3.1 live の状態の扱い
- live の Runtime の状態を変えてよいのは、次の2つだけです。それ以外の、状態を変える API の呼出し、製品の hook や関数の差し替えは禁止です。
- fixture の最初の導入(
restore_snapshotで入力 R を読み込む)。 - 決められた
GameClock.advance_minutes(1)による進行。
- fixture の最初の導入(
- 公開されている読取だけの API(
create_snapshot()など)は、tick の外で呼んでかまいません。その後、live の全状態のvar_to_bytesが、呼ぶ前と完全一致することを確かめます。一致しなければ失敗です。
3.2 tick の中の区間(受動的な観測。scripts/core/simulation_kernel.gd:122-157)
tick_started(m)で tick の ID と、進行の前の分 m を記録します。- APPLY_COMMANDS の
tick_phase_startedは、進行の前の分 m で発行されます。TIME_BOUNDARIES 以降の phase とtick_completedは、進行の後の分 m+1 で発行されます。phase を分で区切らず、tick の ID で結びつけます。 - 区間は、隣り合う信号の間の inclusive な wall 区間として記録します。各 handler の純粋な排他時間だとは主張しません。区間ごとの内容は次のとおりです。
- I0:
tick_started→ APPLY_COMMANDS の開始 - I1: APPLY_COMMANDS → TIME_BOUNDARIES(clock の進行、
_sync_calendar_fields、_handle_time_boundariesを含む) - I2〜I10: TIME_BOUNDARIES → MANA → … → PUBLISH_SNAPSHOT の各開始の間
- I11: PUBLISH_SNAPSHOT →
tick_completed(create_snapshot、同期の signal であるsnapshot_published/state_updatedの受け手、snapshot_versionの更新を含む)
- I0:
- observer 自身の費用: 各 callback の中で、自分の時間を別に計測して記録します。区間からは差し引きません(二重に数えないために、区間は「observer を含む」と明記します)。
- 経路:
PathService.path_query_measured(duration_us)(scripts/world/path_service.gd:4、:985)に受動的に接続し、query の件数と時間を、その時点の区間に結びつけます。これは query の費用だけで、connector の更新や再構築の費用は含みません。query の時間を、connector 全体の費用へ広げて解釈しません。
3.3 tick の外の内訳(独立した inclusive の計測。合算して100%にしない)
- snapshot の変換: live で
create_snapshot()全体を N=5 回、各 service のcreate_snapshot()をそれぞれ N=5 回、別々に計測します。各値は独立した inclusive の計測で、排他的な内訳として足し合わせることはしません。N=5 は r02 の試行数に揃えたもので、R で1回約 80 ms のため負荷が小さいことが根拠です(製品の値ではありません)。 - 検証・コピー・保存・読込: live の状態は使いません。使い捨ての scratch で、公開されている restore・save・load・validator だけを使います。
- scratch に渡すのは、live の snapshot の deep copy です。保存先は private user:// の scratch の path です。
- 各操作を N=5 回計測します。
- 終わった後に、live の全状態が一致すること、scratch から live への参照の漏れや副作用が無いことを確かめます。
- 公開の API だけで scratch を作れない場合(autoload に触れる必要がある場合)は、その項目を理由つきの未測定にして、判断に戻します。
- 粒度: 製品の内部で分けられない変換・検証・コピーは、測れる粒度(たとえば「restore 全体」)のまま記録します。分けたことにはしません。
- 値: 正当に測れた 0 は 0 として記録します。測れなかったものは null と理由にします。欠損を 0 で補うことは拒否します。
3.4 条件と順序
- 入力・engine・machine は P28-01 の R と同じです(入力
eb4f7700…、enginec7cccbf8…、Mac16,10)。headless で実行します。 - 区間: warm 30分、測定30分。別の PID で3回行います。3回の間で、区間の順位が一致したかを記録します。
- 実行の順序(P08/P18 の通常の方式を再利用する):
- worker が S を提出します(局所の unit 試験の全件 PASS つき)。
- Root が、凍結した S を隔離した stage で、import・parse・DIAG の3 PID を native で実行します。
- Root が raw を結合し、bundle を作ります。
- 登録した validation(unit と、bundle に対する native-verify の両方)を実行します。
- 別の新しい Opus が、全4条件と全 raw をレビューします。
- prepare → 候補の source と測定した S の一致の資格確認 → Root の委任による承認 → integration → final。
- native は、測定した実際の S のものとして記録します。最終候補 Q で測ったとは表記しません。資格確認で、Q と S の source・設定・engine・入力が一致することを確かめます。
4. 段階B: P28-02-OPT(DIAG の後に、根拠をもとに登録する)
- 対象: DIAG で実測の上位になったもののうち、原 work の4つの分類(snapshot の変換・検証・コピー・経路接続)に入るものだけです。選ぶのは管制で、DIAG の表を根拠に記録します。上位が4分類の外にあった場合は、原 work の範囲の外なので、判断に戻します。
- 同等性を先に固定します(変更の前に、試験として凍結する):
- E1 全状態:
- まず
var_to_bytesの完全一致を確かめます。 - 一致しない場合に限り、RESTART の修正8で確定した完全な typed の正規表現と比べます。これは、dict の key を文字で並べ(衝突は拒否)、全ての key と値の
typeofと配列の順序と値を保つものです。それで一致すれば「dict の列挙の順序だけの差」として、その path を記録します。 - それ以外の差は不一致です。SimulationDigest だけでの判定は不可です。
- まず
- E2 全イベント:
DomainEventStream.subscribe(&"*", &"p28_02_equivalence_observer", …)(scripts/core/domain_event_stream.gd:5、:17、配送は:76-85)で受動的に記録し、全件を上限なく、順序・型・payload・ID・counter とともに保持します。- subscriber の登録が snapshot を変えないこと(
create_snapshotの範囲の外)を確かめ、終わったらunsubscribeします。 - recent_events(直近256件)の一致だけを、全イベントの一致として扱うことはしません。
- E3 失敗時の拒否: 不正な入力(改ざん、型の違い、欠けた key、範囲外、重複した ID)が、変更の前と同じ code で、原子的に拒否されること。
- E4 キャッシュの古さ: 無効化の全ての経路(コマンド、tick、restore、load)で、古い値を返さないこと。
- E5 所有権: 公開した値を外から変更しても内部に影響せず、その逆も無いこと。
- E1 全状態:
- 比較は2種類に分けて固定します:
- (a) phase の比較: DIAG と同じ区間(headless、
advance_minutes)で、変更の前と後を別の PID で各3回。advance_minutesではperformance_budget.record_simulation_frameが呼ばれず、_samplesが空になるため、この比較では kernel の予算の値を使いません。 - (b) frame の予算の比較: 製品の経路
process_real_deltaを通る、受入済みの collector の windowed の P1(x1)と P2(x3)を、変更の前(現在の Q)と後で、別の PID で各3回。kernel 定義の標本で比べます。
- (a) phase の比較: DIAG と同じ区間(headless、
- RESTART の native の pair を1回行い、保存と再開の再現が保たれることを確かめます。製品の source が変わるので、最終候補で正式の全 roster を1回実行します。
- 予算は P28-02 の受入の条件ではありません。実測をそのまま記録し、false は合格に補いません。同等性 E1〜E5 が1つでも崩れた変更は採用しません。
- 独立レビュー: 原 work と原 acceptance、横断条件6件、補助の U1/U2 を、whole-unit として判定します。
5. 共通の制約
- 予算値、原本の分母、横断条件の原文は変更しません。Runner 本体、権限、認証、課金も変更しません。修正回数の上限は null で、API の課金はしません。容量待ちは再導入しません。