Skip to content

test: assert delivered rate and concurrency match config - #735

Open
Bslabe123 wants to merge 1 commit into
kubernetes-sigs:mainfrom
Bslabe123:test/load-shape-accuracy-633
Open

test: assert delivered rate and concurrency match config#735
Bslabe123 wants to merge 1 commit into
kubernetes-sigs:mainfrom
Bslabe123:test/load-shape-accuracy-633

Conversation

@Bslabe123

@Bslabe123 Bslabe123 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Part of #633. Asserts for the first time that the load inference-perf actually offered matches the load it was configured with: achieved request rate against configured rate, and delivered in-flight concurrency against concurrency_level.

Row in #606: "Load-shape accuracy (#633)", sim tier.

Why this is needed

Every other e2e test checks the measurement side; nothing checks the stimulus side, which sweep silently depends on. docs/loadgen.md promises "the exact concurrency specified"; unit tests cover only the split arithmetic of _set_worker_concurrency, not whether workers honour it over real sockets. A semaphore bug throttling to concurrency_level - 1 passes everything today.

What the test does

  • Faked: the server. llm-d-inference-sim with pinned per-request latencies. Deliberately sim: against real vLLM these assertions would measure the server's capacity, not the load generator.
  • Oracle: the configured load, plus delivered rate and in-flight concurrency reconstructed in the new e2e/utils/load_shape.py from raw per-request start_time/end_time pairs, never from reportgen's summary numbers.
  • Rate (constant and poisson, 600 requests at 40 qps): count and requested_rate exact; reported send_duration and achieved_rate must equal the recomputation (rel 1e-9, so any drift is a reportgen bug); delivered rate within a tolerance derived from the arrival process.
  • Tolerance as a function of n, not a magic number. constant: gaps are rescaled to sum to exactly duration, so the only stochastic term is the first gap, Exp(1)/n relative, k = 12 for ~1e-5 false failure. poisson: renewal time, CoV 1/sqrt(n), 4 sigma. Both floored at 5% for jitter. At n=600: 5% and 16.3%; tightens by raising n, never by shrinking the multiplier.
  • Concurrency (concurrency_level 8 and 5, 2 workers each, so one split leaves a remainder): in-flight is a sweep line over starts and ends, ties resolved ends-before-starts. The plateau window [C-th earliest start, latest start] excludes ramp-up and drain by construction (request k cannot start until k-C finished). Delivered concurrency must never exceed C (exact) and must be within half a slot of C time-weighted (measured handoff deficit on the sim is about 0.04 slot, so 10x headroom while still failing a whole-slot deficit).
  • load_summary["concurrency"] is asserted only as a wiring check, labelled as one (reportgen copies it off the stage config). achieved_rate is not asserted on concurrent stages, which main.py rewrites to rate=num_requests, duration=1.
  • Eight helper self-tests run without the sim and prove the assertions go red: C-1 and C+1 both raise, the tolerance function is checked at known values, the sweep line against hand-computed segments.

Per the row's lane note, dispatch scheduling arithmetic stays unit level in #659; only what needs real processes and sockets is asserted here.

Status

Stands alone on main (7bfbaed).

Verified locally with llm-d-inference-sim v0.6.1: 12 passed for the new module (4 sim-backed, 8 self-tests), 109s serial, 64s at -n 4. pdm run validate clean; unit suite unaffected (889 passed, 15 skipped). Kill-mutant: dropping the concurrency slack to 0.001 fails both cases.

Not yet verified: the CI runner. Tolerances have roughly 10x headroom over what was observed (constant 0.5% off a 5% budget, poisson 1.5% off 16.3%), but a merge-gating timing assertion earns its flake budget over repeated runs.

Limit of the oracle (in the module docstring): the timestamps are the client's own, so this catches a generator falling behind schedule, a semaphore admitting the wrong count, and a wrong reportgen rate, but not a timestamping bug. The #481 absorber records offered load server-side; once it lands this test can be retargeted at it without changing assertions.

Helpers live in e2e/utils/load_shape.py, as #633 asks, so the #632 worker matrix can reuse them.

@kubernetes-prow kubernetes-prow Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Aug 17, 2026
@github-actions github-actions Bot added the do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. label Aug 17, 2026
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Bslabe123

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow kubernetes-prow Bot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Aug 17, 2026
@github-actions github-actions Bot removed the do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. label Aug 17, 2026
@Bslabe123 Bslabe123 mentioned this pull request Aug 17, 2026
64 tasks
@Bslabe123 Bslabe123 changed the title [WIP] test: assert delivered rate and concurrency match config test: assert delivered rate and concurrency match config Aug 17, 2026
@Bslabe123
Bslabe123 marked this pull request as ready for review August 17, 2026 22:17
@kubernetes-prow kubernetes-prow Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 17, 2026
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Aug 18, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from 9b8c938 to f519f9a Compare August 18, 2026 18:19
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Aug 18, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Aug 18, 2026
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Aug 19, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from f519f9a to 2a7f378 Compare August 19, 2026 20:17
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Aug 19, 2026
@Bslabe123 Bslabe123 added the priority/important-longterm Important over the long term, but may not be staffed and/or may need multiple releases to complete. label Aug 20, 2026
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Aug 26, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from 2a7f378 to 5c075ef Compare August 26, 2026 15:50
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Aug 26, 2026
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from 5c075ef to 02a8372 Compare August 26, 2026 17:48
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Aug 26, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Aug 26, 2026
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from 02a8372 to e0d06c6 Compare August 27, 2026 19:38
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Aug 27, 2026
Sim-backed e2e for the stimulus side of the load generator: that the load
actually offered matches the load that was configured. Nothing verified
this before, although docs/loadgen.md states the concurrent load type
"achieves the exact concurrency specified".

Adds e2e/utils/load_shape.py with the reconstruction helpers (in-flight
sweep line, closed-loop plateau window, arrival-process tolerance) so the
worker matrix in kubernetes-sigs#632 can reuse them. Adds a test module with four
sim-backed cases (constant and poisson rate stages, concurrency levels
divisible and not divisible by num_workers) plus eight helper self-tests
that prove the assertions can go red.

Delivered concurrency is reconstructed from the raw per-request start and
end timestamps, not read off load_summary["concurrency"], which reportgen
copies straight from the stage config.

Part of kubernetes-sigs#633.
@Bslabe123
Bslabe123 force-pushed the test/load-shape-accuracy-633 branch from e0d06c6 to 886419c Compare September 3, 2026 20:30
Bslabe123 added a commit to Bslabe123/inference-perf that referenced this pull request Sep 3, 2026
The absorber now records when each reply finished (done_s) alongside the
arrival time and stops keeping its own in-flight counter. The parity test
feeds those arrival/finish pairs to e2e/utils/load_shape.py: rate via
observed_send_rate, concurrency via the sweep line and
assert_delivered_concurrency (peak never above C, time-weighted plateau
mean within half a slot), so there is one definition of each in the e2e
tier.

rate_rel_tol is gone from expected.yaml. Each tool declares its spacing
pattern (tools.<tool>.arrival: constant|poisson) and the tolerance is
rate_tolerance(num_requests, arrival). Case a rises from 40 requests at
8/s to 600 at 40/s, the smallest count at which the Poisson side earns a
useful bound (16%), matching the load-shape accuracy test.

Measured against the absorber: rate 40.01/s (5% budget), plateau mean
7.88 of 8 (half-slot budget); C-1 and C+1 both fail.
@github-actions github-actions Bot added do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. and removed do-not-merge DEPRECATED. Indicates that a PR should not merge. Label can only be manually applied/removed. labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. priority/important-longterm Important over the long term, but may not be staffed and/or may need multiple releases to complete. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant