nnue-train: 教師 pool relabel 用の順序保存 chunk loader と score sidecar writer - #409
Conversation
…r を追加 リスコアの不変条件「sidecar 行 i = 入力 record i」(全行・原順序・無フィルタ) を 満たす並列 decode を提供する。学習用 BucketedPrefetchedLoader は worker >= 2 で 順序非決定 + score-drop skip + epoch wrap のため流用できない。 - OrderedPsvLoader: worker が共有 counter で chunk (= record 範囲) を claim し、 各自 PsvFileLoader::new_range で読んで decode。消費側が seq 順に再整列する。 chunk 内容は record 範囲だけで決まり worker 数・スケジューリング非依存 (決定論)。slot pool の bounded back-pressure と slot recycle は BucketedPrefetchedLoader のパターンを踏襲 - 末尾 chunk は最終 real 行の複製で pad_multiple (GPU tiled kernel の b % 16 == 0 制約) へ padding し、n_real で truncate する契約 - fail-closed: I/O エラー / short read (実行中の入力変化) / active-feature 超過で 以降の chunk を 1 個も yield しない - ScoreSidecarWriter: i16 sidecar の追記 + in-progress/done marker 管理 (fingerprint text 一致時のみ件数ベース resume、奇数サイズ拒否、完了時に 全件数検証して .done へ昇格)。fingerprint の組み立ては rescore driver 側の契約 - テスト 14 本 (CPU-only): 順序・全行 yield・端数 padding・resume 開始位置・ 決定論・torn file / 入力縮小の fail-closed、writer の resume bit 一致・ fingerprint 不一致 truncate・完了 skip・奇数サイズ / 件数不足拒否、 loader+writer 通しの中断→再開 bit 一致 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 検証を追加 - worker のエラーを side slot でなく result channel の Err として送る。side slot 方式は blocking recv 中の消費側/他 worker を起こせず、欠損 seq 待ち + slot 枯渇 で deadlock し得た。エラー受領時は channel を閉じて全 worker を join し、loader を毒化する (以降の next_chunk も同じエラー) - worker 内 panic を catch_unwind でエラーに変換して同経路で伝搬し、channel 送信 など catch 外の panic は join で捕捉する。さらに全 worker 終了時に受領 chunk 数 = 期待 chunk 数を検証し、chunk 欠損が Ok(None) = 正常完了に化けないようにする - decode 済み局面の安価な整合性検証 (validate_board: 玉の存在/一致、駒総数 <= 40) を追加。PackedSfenValue::decode は checked でなく壊れた record も局面に化ける ため、検出可能な破損は硬いエラーにする (完全な合法性検証は行わず、入力が正当な 教師生成物であることを API 契約として明記) - メモリ有界性コメントを実際の根拠 (in-flight chunk が slot を 1 個ずつ占有) に修正 - テスト追加: worker >= 2 のエラーで timeout 内に Err 停止 (deadlock regression)、 panic のエラー伝搬 + 毒化継続、破損 record の fail-closed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 061f95c738
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for &score in scores { | ||
| self.out.write_all(&score.to_le_bytes())?; | ||
| } | ||
| self.written = new_total; |
There was a problem hiding this comment.
Poison the writer after a partial score write
When the underlying write fails after emitting part of this slice—for example, due to a transient filesystem or disk-full error—written remains unchanged even though the BufWriter or file may already contain a prefix. If the caller retries on the same writer, that prefix is appended again (or after a partial i16), and a later finish() can mark the corrupt sidecar complete because it checks only the logical counter. The writer should become unusable after any write error, or otherwise track and validate the physical output before promotion.
Useful? React with 👍 / 👎.
write が slice の一部を出力した後に失敗しても written カウンタは進まないため、 同一 writer で retry すると prefix の二重 append や record 途中への継ぎ足しが 起き、finish は論理カウンタしか見ないため破損 sidecar が .done へ昇格し得た。 - 任意の write エラーで writer を毒化し、以降の write_scores / finish は同じ エラーを返す (loader の poison と同じ思想)。復旧は次回起動の open (件数 ベース resume、2 byte 境界で切れていない末尾は硬いエラー) が担う - finish は flush + sync 後に物理ファイルサイズ == written × 2 を検証してから .done へ昇格する (論理カウンタと物理実体の突き合わせ) - 書き込み先を SidecarSink trait (本番 File) で差し替え可能にし、部分書き込み 失敗を注入するテスト (毒化後の retry / finish 拒否) と、外部混入による物理 サイズ不一致で昇格が拒否されるテストを追加 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
P2 指摘 (部分書き込み後の writer 毒化欠如) を 21e9bcb で修正しました。
検証: Windows fmt / clippy |
概要
GPU リスコアツール (教師 pool PSV を学習済み NNUE の 1-node 静的評価でリラベルする) の土台となる host 側部品を
crates/nnue-train/src/rescore.rsに追加する。リスコアの不変条件は「sidecar 行 i = 入力 record i」の行対応 (全行・原順序・無フィルタ) で、学習用BucketedPrefetchedLoader(worker ≥ 2 で順序非決定 + score-drop skip + epoch wrap) は流用できないため、同じ channel ring / slot recycle パターンの上に順序保存の別実装を置く。設計要点
OrderedPsvLoader: worker が共有 counter で chunk (= 固定 record 範囲) を claim し、各自PsvFileLoader::new_rangeで読んで decode。消費側が seq 順に再整列する。chunk 内容は record 範囲だけで決まり、worker 数・スケジューリング非依存 (決定論)。slot pool の bounded back-pressure で再整列 buffer も有界pad_multiple(GPU tiled kernel のb % 16 == 0制約) へ切り上げ、n_realで truncate する契約Errとして本流で伝搬 (blocking recv 中の消費側を必ず起こす。エラー受領で channel を閉じ全 worker を join して loader を毒化 — 以降の呼び出しも同じエラー) ②worker 内 panic はcatch_unwindでエラーに変換して同経路、catch 外の panic は join backstop で捕捉 ③全 worker 終了時に受領 chunk 数 == 期待 chunk 数を検証 — chunk 欠損が正常終了 (Ok(None)) に化けないPackedSfenValue::decodeは checked ではなく壊れた record も局面に化けるため、validate_board(玉の存在/一致、駒総数 ≤ 40) で検出可能な破損を硬いエラーにする。完全な合法性検証はせず「入力は教師生成パイプラインの正当な PSV」を API 契約として明記ScoreSidecarWriter: i16 sidecar の追記 +.in-progress/.donemarker 管理 (fingerprint text 一致時のみ件数ベース resume、奇数サイズ拒否、完了時に全件数検証して.doneへ atomic 昇格)。fingerprint の組み立ては後続の rescore driver 側の契約テスト (17 本、CPU-only — local-ci の CPU テストで完結)
.doneの作り直し検証
bash scripts/local-ci.shフル PASS (sh11235 / ubuntu-3080ti、全 step)-D warnings/ CPU テスト green (nnue-train 158 passed)後続: CLI 接続と GPU forward 接続 (rescore driver) は別 PR で行う。
🤖 Generated with Claude Code