Skip to content

[None][test] Enable warmup request for gen_only perf sanity lanes - #17098

Merged
nvpohanh merged 4 commits into
NVIDIA:mainfrom
erictsai-nv:test/gen-only-warmup
Aug 6, 2026
Merged

[None][test] Enable warmup request for gen_only perf sanity lanes#17098
nvpohanh merged 4 commits into
NVIDIA:mainfrom
erictsai-nv:test/gen-only-warmup

Conversation

@erictsai-nv

@erictsai-nv erictsai-nv commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Dev Engineer Review

  • ClientConfig adds disabled-by-default warmup support and persists it as b_warmup.
  • Warmup applies only to disaggregated gen_only lanes with concurrency == 1.
  • The command omits --no-test-input only when warmup is enabled.
  • The concurrency restriction prevents a higher-concurrency deadlock at the GEN worker fill gate.
  • Warmup remains excluded from baseline matching.
  • The change is limited to performance sanity configuration. No public API or test-list changes were made.

QA Engineer Review

  • Modified test code: tests/integration/defs/perf/test_perf_sanity.py.
  • No test functions were added, modified, or removed.
  • No test-db/ or qa/ entries changed.
  • Existing performance sanity tests cover the updated configuration path.
  • Verdict: needs follow-up because later CI runs failed.

Description

gen_only perf-sanity lanes measure as few as a single request: iterations is
forced to 1 for gen_only mode. With only one measured request,
one-time cold-start costs land entirely on the measured TTFT. Concretely, with
the KV cache manager V2 (python cache transceiver), the lazy connection setup
(ZMQ mesh + NIXL metadata registration, ~6s) shows up as a fake -43% output
throughput regression on the GLM-5 con1 gen_only lane, while steady-state
performance is actually on par or better than V1.

Fix: stop passing --no-test-input for gen_only lanes, so that
benchmark_serving's built-in initial single-prompt test run (excluded from
metrics) acts as a warmup request.

Validation on the GLM-5 con1 gen_only lane (GB300, 8k1k, tep8 mtp3, KV cache
manager V2 cpp):

measured TTFT output tok/s
without warmup 6.9 s 99.5
with warmup (this PR) 1.72 s 196.8
V1 baseline (no warmup) 2.5 s 176.0

Test Coverage

  • perf/test_perf_sanity.py::test_e2e[disagg_upload-gen_only-*] lanes exercise
    the changed path directly; all other lanes are unaffected (warmup defaults to
    off).
  • Manually validated on
    disagg_upload-gen_only-gb300_glm-5-fp4_8k1k_con1_ctx1_dep2_gen1_tep8_eplb0_mtp3_ccb-NIXL
    (results above).

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

Signed-off-by: Eric Tsai <ertsai@nvidia.com>
@erictsai-nv
erictsai-nv marked this pull request as ready for review July 31, 2026 02:54
@erictsai-nv
erictsai-nv requested review from a team as code owners July 31, 2026 02:54
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5b2cdd33-e370-43ad-b5b2-0b7e57a6e62b

📥 Commits

Reviewing files that changed from the base of the PR and between 67579d8 and 4cf2940.

📒 Files selected for processing (1)
  • tests/integration/defs/perf/test_perf_sanity.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/defs/perf/test_perf_sanity.py

Walkthrough

The performance sanity tests now read and persist a warmup setting. They also change benchmark command generation and disaggregated gen_only lane setup to use warmup in the supported cases.

Changes

Warmup-aware benchmark execution

Layer / File(s) Summary
Warmup configuration and command selection
tests/integration/defs/perf/test_perf_sanity.py
ClientConfig reads the warmup setting and records it as b_warmup. The benchmark command omits --no-test-input when warmup is enabled.
Disaggregated warmup selection
tests/integration/defs/perf/test_perf_sanity.py
Disaggregated gen_only client setup enables warmup for concurrency 1 lanes and keeps it disabled for higher concurrency lanes.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: qijune, zhanruisunch

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the test change and the specific warmup behavior enabled for gen_only performance sanity lanes.
Description check ✅ Passed The description explains the problem, solution, validation results, test coverage, and includes the required checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/integration/defs/perf/test_perf_sanity.py (1)

980-983: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add regression coverage for both command branches.

Test ClientConfig._to_default_benchmark_cmd() with warmup=False and warmup=True. Assert that --no-test-input is present only when warmup is disabled. Also cover the gen_only parser path that sets warmup=True.

As per path instructions, this test review must report changed test functions, test-list membership, and a coverage verdict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/defs/perf/test_perf_sanity.py` around lines 980 - 983, Add
regression tests for ClientConfig._to_default_benchmark_cmd() covering
warmup=False and warmup=True, asserting --no-test-input appears only when warmup
is disabled; also exercise the gen_only parser path that enables warmup. Report
the changed test functions, test-list membership, and the resulting coverage
verdict.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/defs/perf/test_perf_sanity.py`:
- Line 897: Extend ClientConfig.to_match_keys() and ClientConfig.to_db_data() to
include warmup as a persisted boolean so baseline identity distinguishes
warmup-enabled and cold runs. When loading existing rows that lack the field,
treat warmup as false for backward compatibility, and ensure matching uses the
normalized boolean consistently.

---

Nitpick comments:
In `@tests/integration/defs/perf/test_perf_sanity.py`:
- Around line 980-983: Add regression tests for
ClientConfig._to_default_benchmark_cmd() covering warmup=False and warmup=True,
asserting --no-test-input appears only when warmup is disabled; also exercise
the gen_only parser path that enables warmup. Report the changed test functions,
test-list membership, and the resulting coverage verdict.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3da2bcf3-1bd9-47ab-98a5-0dccc44c6cca

📥 Commits

Reviewing files that changed from the base of the PR and between 138eb43 and 7fb102f.

📒 Files selected for processing (1)
  • tests/integration/defs/perf/test_perf_sanity.py

Comment thread tests/integration/defs/perf/test_perf_sanity.py
@BowenFu

BowenFu commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Nice diagnosis of the cold-start TTFT, and the warmup=False default keeps every non-gen_only lane's command unchanged. But I think enabling it for all gen_only lanes deadlocks the ones with concurrency > 1.

gen_only lanes are launched with TLLM_BENCHMARK_REQ_QUEUES_SIZE=<concurrency> on the GEN worker (jenkins/scripts/perf/submit.py, the benchmark_mode == "gen_only" branch). The GEN executor then refuses to run a forward pass until condition (A) of the fill gate holds:

local_all_fetched = (self.num_fetch_requests >= self.benchmark_req_queues_size)

py_executor.py:3790 (and the same predicate at :3580), with can_forward = not self.is_benchmark_disagg at :3992 / :4462.

num_fetch_requests is a plain cumulative counter (py_executor.py:5051) with no warm-up exclusion. benchmark_serving's test-input request is sent alone and awaited before the measured load. So for a lane with concurrency = N > 1:

  1. warm-up request arrives at GEN → num_fetch_requests = 1
  2. 1 >= N is false → gate stays closed → no forward pass
  3. the warm-up request never produces a token, the client never gets its response, and the N measured requests are never sent

Nothing breaks the cycle — the gate is re-evaluated every iteration but (A) can only be satisfied by requests the client is blocked from sending.

con1 is the one case that works, because the warm-up request alone satisfies 1 >= 1. That is exactly the lane you validated on (glm-5-fp4_8k1k_con1_...).

Of the 37 gen_only lanes currently scheduled in tests/integration/test_lists/test-db/*perf_sanity*.yml, only 8 are con1. The other 29 range from con4 to con4301 — including the four DSv4-Pro cases (con8, con180, con666, con4301) that #16961 is currently fixing in this same gate.

Options, roughly in order of preference:

  • exclude the warm-up request from the fill counter (needs an executor-side signal, so it is the invasive one);
  • add 1 to TLLM_BENCHMARK_REQ_QUEUES_SIZE when the lane runs a warm-up — but that changes the measured batch shape;
  • scope warmup to concurrency == 1, which is where the single-request cold-start problem actually lives and where your measurements are.

Also worth coordinating with #16961 — it is rewriting how this fill target is derived (_configure_benchmark_req_queues_size now clamps it to the executor admission capacity), so whichever lands second will need to account for the other.

Non-blocking: CodeRabbit's to_match_keys() / to_db_data() point isn't live today — warmup is derived from benchmark_mode, which is already part of the lane identity, so no run can match a baseline that disagrees on it. It would become live the moment warmup is settable from a lane YAML (ClientConfig.__init__ already reads it out of client_config_data, and the aggregated path at :1834 passes YAML through verbatim). A comment saying "derived only, do not set from YAML" would keep that honest.

I have not reproduced the hang — this is read from the gate source, so please sanity-check it against a con > 1 gen_only run before dismissing it.

…mup flag in perf DB

Signed-off-by: Eric Tsai <ertsai@nvidia.com>
@erictsai-nv
erictsai-nv marked this pull request as draft July 31, 2026 09:01
@erictsai-nv
erictsai-nv marked this pull request as ready for review July 31, 2026 09:54
@erictsai-nv

Copy link
Copy Markdown
Contributor Author

Pushed the option 3 fix (warmup scoped to gen_only + concurrency == 1, plus the "derived only" comment you suggested).

After pushing, though, I started questioning my own assumption that con1 is the only exposed shape — so I probed it: I cloned the con1024 lane config with only concurrency_list changed, and ran v1 vs v2 (KV cache manager v2, python transceiver) without warmup:

con v1 mean TTFT v2 mean TTFT delta
1 2.54 s 6.92 s +172%
4 3.21 s 7.60 s +137%
16 5.33 s 8.00 s +50%
1024 243.2 s 244.9 s +0.7%

(TPOT is flat or slightly better on v2 across the board — the gap is entirely the one-time connection setup landing on the TTFT baseline, which scales with concurrency × ISL / ctx throughput.)

So the honest framing is: the cold-start problem lives on any lane with a small con × ISL product, not just con1. Today's gen_only lanes happen to be either con1 or minutes-scale baselines (con64 @ 128k, con256 @ 32k, con512+ @ 8k), so the con1-only scoping covers current reality. But if lanes like con4/con16 @ short ISL are expected in the future, I think we'd need option 1 (executor-side warmup exclusion) as the durable fix. What do you think?

(On option 2 — re-reading "add 1 to TLLM_BENCHMARK_REQ_QUEUES_SIZE when the lane runs a warm-up": if that means setting the queue size to concurrency + 1 on warmup-enabled lanes, I don't think it resolves the serial wait — the client blocks until the warmup request completes, so the gate never sees more than 1 queued request no matter how large the queue size is. Let me know if I'm misreading it.)

@BowenFu BowenFu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option 3 looks right, and your read of option 2 is correct — concurrency + 1 doesn't help, because the client awaits the warmup response before sending the measured load, so the gate never sees more than 1 queued request regardless of the queue size. Scratch that option.

On the durable fix: agreed that con4/con16 @ short ISL would need option 1, but I wouldn't take the executor change now — every gen_only lane in tree today is either con1 or a minutes-scale baseline, so the con1 scoping is exact rather than merely convenient, and the comment you added is what keeps it honest. Worth revisiting when such a lane is actually proposed.

Verified: --no-test-input is still unconditionally seeded and only dropped under if not self.warmup, so every other lane's command is byte-identical; b_warmup in to_db_data() without a match key is safe since warmup is fully determined by s_benchmark_mode + l_concurrency, both already match keys. Not blocking on the one-time TTFT step change those 8 con1 lanes will show against their stored baselines.

@tburt-nv

Copy link
Copy Markdown
Collaborator

I'm not familiar with this code, I'll defer to other reviewers in the trt-llm-qa review group

@tburt-nv
tburt-nv removed their request for review July 31, 2026 18:26
@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63277 [ run ] triggered by Bot. Commit: 0b3f3df Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63277 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63317 [ run ] triggered by Bot. Commit: 0b3f3df Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63317 [ run ] completed with state FAILURE. Commit: 0b3f3df
/LLM/main/L0_MergeRequest_PR pipeline #51312 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63364 [ run ] triggered by Bot. Commit: 3dbba98 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63364 [ run ] completed with state FAILURE. Commit: 3dbba98
/LLM/main/L0_MergeRequest_PR pipeline #51351 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63917 [ run ] triggered by Bot. Commit: 4cf2940 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63917 [ run ] completed with state SUCCESS. Commit: 4cf2940
/LLM/main/L0_MergeRequest_PR pipeline #51856 completed with status: 'UNSTABLE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

Link to invocation

@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63995 [ run ] triggered by Bot. Commit: 4cf2940 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63995 [ run ] completed with state FAILURE. Commit: 4cf2940
/LLM/main/L0_MergeRequest_PR pipeline #51930 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@erictsai-nv

Copy link
Copy Markdown
Contributor Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64157 [ run ] triggered by Bot. Commit: 4cf2940 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64157 [ run ] completed with state SUCCESS. Commit: 4cf2940
/LLM/main/L0_MergeRequest_PR pipeline #52075 completed with status: 'SUCCESS'

CI Report

Link to invocation

@nvpohanh
nvpohanh merged commit 77bc7f0 into NVIDIA:main Aug 6, 2026
13 checks passed
chenfeiz0326 added a commit to chenfeiz0326/TensorRT-LLM that referenced this pull request Aug 25, 2026
…anes (NVIDIA#17098)"

This reverts commit 77bc7f0.

Signed-off-by: chenfeiz0326 <203214996+chenfeiz0326@users.noreply.github.com>
chenfeiz0326 added a commit to chenfeiz0326/TensorRT-LLM that referenced this pull request Sep 1, 2026
…anity lanes

NVIDIA#17098 enabled benchmark_serving's initial test request as a warmup for
gen_only lanes; NVIDIA#18011 reverted it, because gen_only does not measure TTFT and
the extra ctx->gen handover leaves a stale mSenderFutures entry that the CTX
worker's blocking idle KV-transfer poll then waits on.

Two other disagg lanes do want it, for two reasons that come to the same thing
-- a one-time cold-start cost that otherwise lands inside the measured window:

* e2e pays for the KV cache transceiver's lazy connection setup (ZMQ mesh +
  NIXL metadata registration) on the first handover, so until that has happened
  once the transfer runs well below steady-state bandwidth.
* ctx_only forces osl=1, so the first cold prefill lands directly in the
  headline TTFT with nothing to amortize it.

The initial test request is excluded from the reported metrics and reuses
input_requests[0], so it carries the lane's own ISL/OSL -- which is what makes
it an effective warmup rather than a token-sized probe. The effect scales as
setup_cost/num_requests: measured on GB300 disagg e2e lanes, median TTFT drops
~49% at 8 requests and ~0.26% at 10240, so short lanes gain and long ones are
unaffected.

warmup is passed to ClientConfig as a constructor argument rather than through
client_config_data, so no lane yaml can enable it. b_warmup is reported but is
deliberately not a baseline match key -- warmup is a measurement-quality knob,
not part of case identity, and forking history would hide the improvement in
its own series -- and that is only sound while the value stays fully determined
by benchmark_mode. b_warmup records the effective value: to_cmd dispatches to
three builders and only the built-in benchmark_serving one has a test request
to suppress, so a warmup requested on an agentx or nv_sa lane is recorded as
False rather than claiming a warmup that never ran.

Signed-off-by: chenfeiz0326 <203214996+chenfeiz0326@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants