4. Run the measurement points¶
Produces: one run folder per Pareto point, with accuracy results at the required points.
Before you begin
- Completed 3. Plan your Pareto curve
- You have a list of legal concurrency levels
- The endpoint under test is up and reachable
- You know which seed set you are binding to — see Seeds below
What you'll do¶
- Probe the endpoint
- Write one YAML config per measurement point
- Run each point at fixed concurrency for its region's minimum duration
- Run accuracy validations at the four mandatory region points
- Keep every run folder
This is the expensive step. Everything up to now took minutes. This takes days.
The binding run constraints¶
| Constraint | Requirement |
|---|---|
| Load pattern | concurrency only. max_throughput and poisson are not valid for Pareto points |
| Steady-state duration | 600 s in Ultra Low Concurrency; 1,200 s in Low, Medium and High — measured over the steady-state window's issue time, not wall clock |
| Completed queries | At least one full pass over the dataset, and the total samples issued must be a whole-number multiple of the dataset size |
| Streaming | stream_all_chunks: true for every performance run |
| Sampling | Performance runs sample with replacement; accuracy runs without |
| Warmup | Optional, max 24 h per point, fully excluded from metrics, and fully documented |
| Consistency | Same model, endpoint config, software stack and seed set across every point |
Run longer than the minimum
Your official numbers now come from a steady-state window the tooling detects inside the run, not from the whole run. A run that only just clears its minimum can fail to produce a usable window, and then the published number falls back to the whole-run average — ramp-up and drain included, which understates what your system does. See What your numbers are measured over.
Configuration consistency is checked across the whole curve
Every point must describe the same system, the same model and the same dataset. A curve assembled from points run against two different software versions will be flagged and can be rejected. Lock your software stack before the first point and don't change it until the last one is done.
Steps¶
1. Probe the endpoint¶
uv run inference-endpoint probe \
--endpoints http://your-endpoint:8000 \
--model <your-model>
2. Generate and adapt a config template¶
uv run inference-endpoint init concurrency
A minimal concurrency config looks like this:
name: point-c64
type: online
model_params:
name: "<canonical model id>"
datasets:
- name: perf
type: performance
path: "<performance dataset path>"
settings:
runtime:
scheduler_random_seed: <from your bound seed set>
dataloader_random_seed: <from your bound seed set>
load_pattern:
type: concurrency # the ONLY valid pattern for a Pareto point
target_concurrency: 64
timeouts:
endpoint_response_idle_timeout_s: 300
endpoint_config:
endpoints:
- "http://your-endpoint:8000"
Validate the config before running it:
uv run inference-endpoint validate-yaml -c point-c64.yaml
3. Run each point¶
uv run inference-endpoint benchmark from-config --config point-c64.yaml
from-config has a narrow CLI surface
It accepts only --config, --timeout and --mode. There's no --report-dir override, so set
report_dir in the YAML if you need to control where artifacts land.
Repeat for every concurrency level in your plan. Each writes its own run folder.
4. Run the accuracy validations¶
You need accuracy results at the four mandatory region points — Ultra Low, Low, Medium and High Concurrency — plus one more if you submit Offline results. All of them use the same endpoint configuration, model weights and software stack as the performance runs.
For a single-turn benchmark, each accuracy run goes at the same concurrency as its point, on the same instance, immediately after that point's performance run. Don't batch them up at the end: the rule ties each accuracy run to the performance run it follows. Every result has to pass.
For a multi-turn benchmark, only the average of the results has to pass, and the concurrency can differ — these runs are expensive, so the rules give you room here.
The simplest way to get the ordering right is --mode both on each of those points' configs, so
one invocation writes the performance run and then the accuracy run into the same report
directory, with accuracy/accuracy_results.json alongside performance/result_summary.json.
Accuracy has no tolerance
Throughput results get reproducibility margins. Accuracy doesn't. It's a hard gate at automated compliance and throughout review. Miss the quality target and the submission is rejected.
5. Keep everything¶
Each run folder contains:
<report_dir>/
├── config.yaml # resolved config as run (secrets redacted)
├── report.txt # human-readable summary
├── events.jsonl # per-event log (the large one)
├── sample_idx_map.json
├── performance/result_summary.json # written when the performance phase ran
├── accuracy/accuracy_results.json # written when the accuracy phase ran
└── metrics/final_snapshot.json
Full detail in Submission package layout.
Warmup logs must be retained
All requests issued before TEST_STARTED are warmup and must not appear in any reported
metric, but their logs must be retained and available for reviewer inspection. Reviewers
may cross-check them against the performance dataset.
Seeds and salting¶
Your submission binds to exactly one seed set, chosen when it first appears, and every point must record that same set. The seeds drive the client's request-issue / sample-order RNG and the per-query salt. Once bound, the set stays valid for that submission even after newer sets are published. Seed rotation never forces an in-flight submission to be re-run.
The salt is a per-query unique value inserted between the shared system prompt and the per-query user context. It is what makes blanket cross-query KV-cache reuse legal: the only prefix two queries can share is the system prompt itself. Accuracy runs use the un-salted dataset so that model output matches the canonical implementation exactly.
Warmup must not use performance-dataset samples
Warmup requests must not use any sample from the benchmark performance dataset — directly, as a subset, truncated, or derived. The accuracy dataset and other sources are fine. If your client does use the performance dataset during warmup, salting must be enabled, and note that the salting flag is not on by default.
The v1.0 seed set is published
As of 2026-09-15 the policies repo carries seedset.yaml: one set, id: A, published for
cohort 2026-10-C1. Its three values are identical to the set the checker already ships,
so a submission binding set A is using the right numbers.
seed_set: A
target_cohort: 2026-10-C1
The checker's copy is stale, so adoption still reports SKIP
The checker's mirror carries cohorts: [] and a comment describing the upstream PR as open —
it predates the merge. With no cohort keys, seed-set-adoption reports SKIP instead of
passing, so a clean report is not confirmation that your cohort is in the adoption window.
Point the checker at the published file to get a real result:
export MLPERF_ENDPOINTS_SEED_SETS=/path/to/seedset.yaml
# or: submission-checker ... --seed-sets /path/to/seedset.yaml
Note the two files are shaped differently — the published one nests under a cohort: key while
the checker's is a bare seed_sets: list. If the override is rejected, reshape it. Tracked as
B4 in Open questions.
Verify¶
For each run folder, confirm the phase directory you expected exists and the run completed:
ls <report_dir>/performance/result_summary.json
python -c "import json;d=json.load(open('<report_dir>/performance/result_summary.json'));print(d['complete'], d['n_samples_completed'], d['duration_ns']/1e9)"
Check three things:
completeistrue— afalsemeans the run drained out or was interrupted, and the point is not usableduration_nsmeets your region's minimum in secondsn_samples_completedrepresents at least one dataset pass
Also confirm the run used the right pattern. run_config in result_summary.json should show the
concurrency load pattern at your target level.
Next¶
→ 5. Author the disclosure files
Problems? See Troubleshooting.