Skip to content

[Engine] Calibrate: survive an engine dying mid-measurement; adaptive gate: say where the copies stopped (#1337, #2188) - #3317

Closed
dsenchankau wants to merge 2 commits into
Niko1221:mainfrom
dsenchankau:preserve-calibration-results-after-crash
Closed

dsenchankau wants to merge 2 commits into
Niko1221:mainfrom
dsenchankau:preserve-calibration-results-after-crash

Conversation

@dsenchankau

Copy link
Copy Markdown
Contributor

Issue: extends #1337 (shipped in 0.1.41); diagnostics for #2188 (still open)

Problem and result

--calibrate restarts the engine for every setting it measures. Since 0.1.41, a candidate whose engine won't start just skips that candidate and keeps what was measured so far (#1337). But an engine that starts fine and then dies during its own measurement still fails the whole calibration.

I hit this on a UD-Q4_K_XL run: --adapt-swaps 80 measured fine, then the --adapt-swaps 160 engine died at the adaptive tier's copy gate (the same error as #2188). About twenty minutes of --pcie-frac and --spec-min-p results were lost.

After this change a dead engine drops only its own candidate. The earlier settings survive and the winner is picked from what actually measured; in my run 80 swaps beat the default by 10% and was still written even though 160 died. The engine's timeout line for that gate now also prints how far the copies got, which is what's missing in #2188.

Related discussion

#1337 (closed; this extends its rule from failed starts to mid-measurement deaths) and #2188 (open; the engine part here is diagnostics only, it fixes nothing). I don't think this needs a scope agreement first: a calibration where nothing dies behaves exactly as before, and the new C++ code only runs after a wait has already timed out.

Approach

Steps 4, 5 and 6 (worker counts, expert tier, the AMD shared-expert fork) already had the failed-start rule through restart() and failed_starts. I routed a mid-measurement death through the same path and folded the three nearly identical blocks into one restart-and-measure helper. Step 6 had no mid-measurement protection at all and failed the whole run the same way. The first start (steps 1-3) still fails the run; nothing has been measured yet, so there is nothing to keep.

On the engine side, when the adaptive gate's wait times out the message now carries the gate's reached counter, its atomic, and the copy stream's pinned value, and says that the #267 release drains the window's own streams and never the tier's copy stream. With STRATA_VERIFY_TRACE=1 it also dumps the window's trace. Nothing changed for the case where the wait succeeds.

Validation

Revision eb07e55. Ubuntu 24.04, i7-13700F, RTX 4080 SUPER (sm_89), CUDA 13.4 (nvcc V13.4.92, cuda_13.4.r13.4/compiler.38855100_0). I re-ran everything on the upstream main commit this branch is rebased on, as a baseline, and the failing lists below are the same on both.

Passed:

cmake -G Ninja -S . -B build-tests -DCMAKE_BUILD_TYPE=Release \
  -DSTRATA_ENABLE_CUDA=ON -DSTRATA_BUILD_TESTS=ON \
  -DSTRATA_Q6K_EXPERTS=ON -DSTRATA_MMQ_KQUANTS=ON -DCMAKE_CUDA_ARCHITECTURES=89
cmake --build build-tests -j "$(nproc)"
ctest --test-dir build-tests --output-on-failure

Build is clean. ctest: "96% tests passed, 6 tests failed out of 149". expert_multi_test skips itself on this CPU (no AVX-512). The six failures (ple_parity, ggml_tq_overlay, native_hadamard_test, tq_cpu_parity, tq_cpu_parity_avx2, isa_guard_check) are the same six on the baseline, same command, same build dir. Nothing new fails on this branch.

PYTHONPATH=. .venv/bin/python tools/test_calibrate.py

33 tests, OK. The baseline has 30; the three new ones cover the cases above: a tier candidate dies and the faster candidate still wins, a worker count dies and the rest still measures, a fork candidate dies and the run still returns.

for t in tools/test_*.py serve/test_*.py bench/test_*.py; do
  PYTHONPATH=. .venv/bin/python "$t" >/dev/null 2>&1 || echo "FAILED: $t"
done

Six failures, the same six on the baseline: test_iq_pack, test_nvfp4_local_pack, test_setup_download, test_setup_engine_hash, test_setup_golden, test_gfx906_qsa_query_swizzle_guard. All environmental, none caused by this change.

Failed: the ctest and python failures listed above. Every one of them reproduces the same way on the baseline, so none comes from this change.

Not run:

Limits

The python tests exercise calibrate.py against a fake engine; nothing crashes a real engine end to end in the tests. And this doesn't fix #2188, it only prints the values its diagnosis needs.

…easurement (Niko1221#1337 extended)

An engine that exits during its own measurement in the worker counts, the
expert tier or the shared-expert fork is now that candidate losing, like a
failed start already is since 0.1.41: the reason goes into the report's
failed_starts list, the other candidates are still measured, and the
settings the earlier steps chose are written.  A run where no engine dies,
and the first start (nothing measured yet), are unchanged.  Steps 4-6 share
one measure_candidate(); step 6 had no mid-measurement protection at all.
The verify-side gate timeout also says where the tier's copy stream
stopped (gate words + STRATA_VERIFY_TRACE breadcrumbs) - the open AMD
report on Niko1221#2188 lacked exactly that.  Tests: tools/test_calibrate.py 30/30
(three new).
@dsenchankau

Copy link
Copy Markdown
Contributor Author

Supersedes #2935 as GitHub refuses to reopen it after the rebase (Validation Failed)

Niko1221 pushed a commit that referenced this pull request Oct 27, 2026
…te (#3317)

#1337 made a failed engine start in a later calibration step a candidate
that loses. An engine that started and then exited during its own rate
measurement still failed the whole run. measure_candidate() now treats
both the same: the candidate is dropped and recorded in
report["failed_starts"], the earlier results stay. It is used for the CPU
worker step, the adaptive expert tier step and the AMD shared-expert
fork. An operator cancel (CalibrationCancelled) is still raised. The first
start (steps 1 to 3) still fails the run.

src/core/verify.cpp: when adapt_gate_wait times out after 20 s, the
message now says which layer was reached (atomic and copy signal), that
the copy stream is not drained by the release, and dumps the
STRATA_VERIFY_TRACE breadcrumbs when that is on. Diagnostics only, #2188
stays open. The watchdog label is set only once a wait actually starts,
so a gate that is already open costs nothing extra (changed from the
PR, which set it on every call).

Tests: tools/test_calibrate.py 33 tests OK (3 new death tests).
Not covered: engine compile of verify.cpp is checked in the build step.
Niko1221 added a commit that referenced this pull request Oct 27, 2026
@Niko1221

Copy link
Copy Markdown
Owner

Thank you. Fixed in 0.2.7: An engine that dies mid-measurement drops only its candidate. Your name is on the commit.

Release: https://github.com/Niko1221/Strata/releases/tag/v0.2.7

@Niko1221 Niko1221 closed this Oct 27, 2026
@dsenchankau

Copy link
Copy Markdown
Contributor Author

@Niko1221 that's actually a false statement. My name is nowhere to be found.

  • This PR is closed, not merged. And because of that I'm not on the contributors list.
  • Your commit 8486336 which merges 7 community-originated PRs lists no names.
  • The 0.2.7 release message lists basically no names and doesn't even mention this PR. And likely some or all of the other 6 community PRs from your merge commit.

MistVVK added a commit to MistVVK/XeStrata that referenced this pull request Oct 30, 2026
Niko1221#1337 で後の段のエンジンの起動の失敗は負けた候補になったが、起動した後に自分の測りの途中で終わった
エンジンは、まだ全体を失敗させていた。measure_candidate() で両方を同じに扱い、候補を外して
report["failed_starts"] に残し、前の結果は保つ。CPU のワーカー数と適応のエキスパートの段で使う。
最初の起動(1〜2 段目)は前のとおり全体の失敗。src/core/verify.cpp の部分(adapt_gate_wait の
時間切れの説明)は XeStrata に無い関数なので移さない。upstream Niko1221#3317。
ruc1j added a commit to ruc1j/XeStrata-890M that referenced this pull request Oct 31, 2026
…の 1)

serve の変更
- 宣言していない名前の tool call を文のまま返すとき、その名前を strata_rejected_tool_calls で答えに添え、/metrics/prometheus で数える(upstream 805ebe1、Niko1221#3796)
- 名前空間つきの tool は namespace=member、namespace_member、member だけの綴りでも受け、`__` を `_` に潰した綴りも宣言した tool として読む(upstream fd8d37e、805ebe17b、Niko1221#3972)
- Chat Completions の tool の結果を、答えた call の順に並べ直す(upstream 11237f8、Niko1221#3395)
- message のない chat の要求は 400 にする(upstream 8a3ca07)
- --host :: で IPv6 で待ち受ける(upstream eba3106、Niko1221#3212)
- /v1/responses の max_output_tokens は 0 と -1 を上限なしとして受け、答えに timings を付ける(upstream 9c2d35d、a0fc88abd、Niko1221#4036、Niko1221#3291)
- response_format の repair_latex_escapes で、LaTeX のエスケープで壊れた JSON を直してから検査する(upstream 2cc6ea1、Niko1221#2893)
- 1 トークンの答えには decode の速さを出さない(upstream 5d5c26d)
- elastic な画像エンコーダーが起動できなければ 503 を返す(upstream 092c7d1、Niko1221#2435)
- lifeline の識別は子プロセスの開始時刻にし、sampler は壊れた source に耐える(upstream b3db5a1、Niko1221#3590、Niko1221#3407)
- web ページ: 答えを追うのは読み手が離れていないあいだだけ(upstream 89a68d8)、広い窓を使い狭い画面に収める(upstream ad1a310、Niko1221#3176)

setup の変更
- cmake と ninja は venv のものを先に取り、CMakeLists.txt が求める 3.24 より古い cmake は使わない(upstream 5bfce4b、Niko1221#3822)
- 別の generator で configure された build フォルダーは作り直す(upstream bf994ca、Niko1221#4038)
- ダウンロードの長さが取れなければ公開されている大きさを使う(upstream a1d2fd7、Niko1221#2287)
- 2 分データが来ないダウンロードは止まっていると言う(upstream ed0c893、Niko1221#3567)
- --rope-scale は有限で 1 以上の値だけ受ける(upstream a332cc9、Niko1221#3393)
- pip の記録はどの OS が書いたかを覚え、別の OS の記録は信じない(upstream fb3bae2、Niko1221#3634)

tools の変更
- gguf_reader.py はヘッダーの読みに上限を置き、壊れた metadata を ValueError にする(upstream 409f246、Niko1221#2288、Niko1221#1919)
- strata_bench.py は amd-smi しかない ROCm 7 以降でも AMD のカードの名前を書く(upstream c471588、Niko1221#3630)
- calibrate.py は計測の途中でエンジンが落ちても、その候補だけを落として続ける(upstream 41188ec、Niko1221#3317)
- chat.py は端末で fenced code block に色を付ける(upstream 3e81173、PR Niko1221#1895)
- 試験: llama.cpp の gguf-py がなければ test_iq_pack は skip、ほか試験の穴 3 つ(upstream 5b14fe2、Niko1221#3236)

fork に合わせて変えたこと: upstream の docs/DETAILS.md の差分は取らず、文書は別に書く。logprobs(upstream 9ea0449)は fork にすでにあり、allowed_token_ids は見送り。Control(strata_control)に依る setup の変更と、fork にない機能(presets の警告、Delete a model、queue timeout の badge)は取らない。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants