Files
Strata/tests/core/serve_stop_bound_test.cpp
hanchengme 243fdb213c serve: a request commits only the rows it can emit, so the parked snapshot matches what the client got (#4677)
In the single-request speculative paths the accepted rows of a verify window were committed first and the output loop cut to max_new or EOS afterwards, so a request's last window could park a snapshot with tokens the client never received. conversation_prefix() needs the whole snapshot to be a prefix of the next prompt, so the snapshot was refused and the older checkpoint was used (about 6% of a 12K-token prompt read again).
serve_stop_bound() caps the accepted rows at the rows the request can still emit and ends at the first EOS, before pl_commit_async. Merged prompt windows keep committing every row. HIP only (STRATA_USE_HIP), where it was measured.
Patch and policy test (2,736 cases against the old loop) by hanchengme, from the issue's attachment. Two changes on our side: it is on by default, STRATA_SERVE_STOP_BOUND=0 turns it off; the test is registered in CMake.
Measured on 4 x R9700, Qwen3.8-Flash-Next IQ3_S, 2 replicas x 2 cards, --batch 8 --batch-groups 2, four 11-15K-token conversations with 768-token answers, conversations opened one at a time: 4 of 16 continuations fell back to the older checkpoint without it, 0 of 16 with it (reuse 98.36% against 99.74%). Opened together as batch rows both builds miss the same continuations (slot overwritten before it is parked); not changed here.
2026-11-01 12:21:56 +01:00

32 lines
1.6 KiB
C++

// (asserts stay on in a Release build: the test is its own check)
#undef NDEBUG
#include "strata/core/serve_stop_bound.hpp"
#include <cassert>
#include <iostream>
#include <vector>
int main() {
int cases = 0;
// Independent reference: the existing emission loop, including its budget and EOS rules.
for (int accepted = 1; accepted <= 16; ++accepted)
for (int remaining = 1; remaining <= 18; ++remaining)
for (int eos = -1; eos < accepted; ++eos) {
std::vector<int> outputs(accepted);
for (int i = 0; i < accepted; ++i) outputs[i] = i == eos ? -1 : 100 + i;
std::vector<int> reference;
for (int i = 0; i < accepted && static_cast<int>(reference.size()) < remaining; ++i) {
reference.push_back(outputs[i]);
if (outputs[i] == -1) break;
}
const int kept = strata::core::serve_stop_bound(accepted, remaining, outputs.data(),
[](int t) { return t == -1; });
assert(kept > 0 && kept <= accepted && kept <= remaining);
assert(std::vector<int>(outputs.begin(), outputs.begin() + kept) == reference);
// Unlike the old full-window commit, no unexposed input suffix enters the cached history.
assert(kept == static_cast<int>(reference.size()));
++cases;
}
assert(strata::core::serve_stop_bound(4, 0, static_cast<const int*>(nullptr), [](int) { return false; }) == 0);
std::cout << "SUMMARY stop_bound cases=" << cases << " passed\n";
}