Skip to content

test(ci): reclassify CPU-only sglang unit tests from gpu_1 to gpu_0 (OPS-8033) - #12718

Merged
nvda-mesharma merged 9 commits into
mainfrom
dtokarev/sglang-cpu-unit-tests-gpu0
Aug 6, 2026
Merged

test(ci): reclassify CPU-only sglang unit tests from gpu_1 to gpu_0 (OPS-8033)#12718
nvda-mesharma merged 9 commits into
mainfrom
dtokarev/sglang-cpu-unit-tests-gpu0

Conversation

@dmitry-tokarev-nv

@dmitry-tokarev-nv dmitry-tokarev-nv commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The sglang pre_merge amd64 GPU job's parallel stage hit ~29.4 min (the 30-min step cap) while executing zero seconds of recorded test time. The wall clock went into 353 items — 9,496 cumulative seconds — trickling through the VRAM-aware scheduler at effective concurrency ~5.4 with the GPU idle (GPU0: 0.5/22 GiB, 350+ queued). 89% of that cumulative time is CPU-only unit tests marked gpu_1, each paying ~24s of per-item scheduling + fresh-interpreter sglang/torch import.

This moves the eight CPU-only files to gpu_0, where the loadscope-parallel stage amortizes imports per worker (existing gpu_0 stage: 205 tests in 96s).

Linear OPS-8033

Details

Observed on PR #12051's run 31036884612 (job 92413246330), from the stage's own telemetry and junit artifacts:

file tests cumulative time in the VRAM-scheduled stage
frontend test_sglang_processor_unit.py 188 4,439s
sglang test_sglang_unit.py 82 2,235s
frontend test_sglang_tool_calls.py 22 528s
sglang test_sglang_multimodal_embedding_cache.py 17 480s
frontend test_sglang_processor_api.py 16 363s
sglang test_sglang_nvdec_video_decoder.py 9 261s
frontend test_sglang_multimodal_prepost.py 5 109s
sglang test_fpm_contract.py 2 42s
  • The gpu_1 marks were a proxy for "needs the sglang image" — test_sglang_unit.py said so verbatim, and test_fpm_contract.py carried profiled_vram_gib(0) alongside gpu_1. Per .ai/pytest-guidelines.md, gpu markers mean GPUs required.
  • The proxy's fear is empirically obsolete: the arm64 CPU-only lane already runs pre_merge and sglang and gpu_0 (205 passed / 96s in the same run) and imports these same packages (test_sglang_decode_handler, test_sglang_multimodal_video, …).
  • The one CUDA-touching test (test_frames_are_pinned_on_cuda) keeps its skipif not torch.cuda.is_available() and stays consistent with gpu_0 = "no GPU required": it self-skips on the CPU lane and still guards pinning on the GPU job's gpu_0 stage.
  • Serve deployments and router e2e keep gpu_1 — they are the stage's genuine GPU work (667s + 212s cumulative) and now get the scheduler to themselves. Expected parallel-stage wall drops from ~29 min to well under 10.

Where should the reviewer start?

Any of the eight pytestmark blocks — the change is identical in each; the comment above the mark documents the reasoning in place.

Validation

  • amd64, GPU masked (NVIDIA_VISIBLE_DEVICES=void), run from /workspace like CI: all eight files — 351 passed / 1 skipped / 0 failed serially, identical result under -n auto --dist=loadscope in ~21s wall (the same tests cost ~8,400 cumulative seconds in the VRAM-scheduled stage). The one skip is test_frames_are_pinned_on_cuda skipping exactly as the gpu_0 contract requires. --collect-only -m "pre_merge and sglang and gpu_0" selects all 352.
  • The no-GPU run surfaced a real latent dependency this PR also fixes (second commit): 14 test_sglang_unit.py tests failed on an accelerator-less host because parse_args reaches SGLang's ServerArgs.__post_init__, whose device auto-detection ends in NotImplementedError when no accelerator exists. The arm64 CPU lane never caught it because the old gpu_1 mark kept the file off that lane. An autouse fixture sets SGLANG_USE_CPU_ENGINE=1 (SGLang's public CPU knob, verified at v0.5.16) only when CUDA is absent, with lru_cache clears on both probes; GPU hosts exercise the real detection path unchanged.
  • ruff clean; GPU-use audit per file: zero direct CUDA references except the self-skipping pinned test.
  • Measured by this PR's own CI (run 31049499476, green, zero failures): the sglang amd64 test job finished in 22 minutes (was 38, with the parallel stage pinned at the 30-min cap). Stage shape after the move: gpu_0 loadscope stage 557 passed in 87.6s (absorbing the ~350 moved tests), VRAM-scheduled stage down to 12:09 with only genuine GPU work, serial tail 4:14. The arm64 CPU lane ran the moved files for the first time and passed in ~4 min, exercising the ServerArgs CPU fixture.

Related Issues

Relates to the timeout observed on #12051.

🤖 Generated with Claude Code


Open in Devin Review

Summary by CodeRabbit

  • Tests
    • Updated test scheduling markers so CPU-only tests run in the appropriate CPU lane without reserving GPU VRAM.
    • Clarified test annotations for CPU-only execution and CUDA-related behavior.
    • Preserved existing test logic and other execution markers.

The sglang pre_merge amd64 GPU job's parallel stage brushed the 30-min
step cap while its junit recorded zero seconds of test time. The stage's
scheduler telemetry shows where the wall clock went: 353 items, 9,496
cumulative seconds, effective concurrency ~5.4, GPU idle at 0.5/22 GiB.
89% of that time is CPU-only unit tests marked gpu_1, each paying ~24s
of per-item scheduling plus a fresh-interpreter sglang/torch import --
the same class of test runs in ~0.1s under the gpu_0 stage's
loadscope-parallel pytest, which amortizes imports per worker.

The gpu_1 marks were a proxy for "needs the sglang image", not for GPU
use -- one file says so verbatim, another carries profiled_vram_gib(0)
alongside gpu_1. The marker guidelines define gpu markers as the number
of GPUs a test REQUIRES, and the proxy's fear is empirically obsolete:
the arm64 CPU-only lane already runs `sglang and gpu_0` (205 passed in
96s) importing these same packages.

Move the eight CPU-only files to gpu_0. The single CUDA-touching test
(test_frames_are_pinned_on_cuda) keeps its skipif and stays consistent
with gpu_0 = "no GPU required": it self-skips on the CPU lane and still
guards pinning on the GPU job's gpu_0 stage. Serve deployments and
router e2e keep gpu_1; they are the stage's real GPU work (667s + 212s
cumulative) and now have the scheduler to themselves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
@dmitry-tokarev-nv
dmitry-tokarev-nv requested review from a team as code owners August 5, 2026 21:17
@github-actions github-actions Bot added test backend::sglang Relates to the sglang backend frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` labels Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The SGLang test modules now use the gpu_0 marker instead of gpu_1. Comments document CPU-only execution and CUDA-specific scheduling behavior.

Changes

SGLang test scheduling

Layer / File(s) Summary
CPU-only test markers
components/src/dynamo/frontend/tests/test_sglang_*.py, components/src/dynamo/sglang/tests/test_*.py
The test modules use gpu_0 and retain their existing markers. Comments document CPU-only execution and CUDA pinned-memory test behavior.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly describes reclassifying CPU-only SGLang unit tests from gpu_1 to gpu_0.
Description check ✅ Passed The description explains the motivation, implementation, reviewer focus, validation, and related issue for the change.

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

🤖 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 `@components/src/dynamo/sglang/tests/test_sglang_nvdec_video_decoder.py`:
- Around line 31-36: Update the module-level pytest marker in
test_sglang_nvdec_video_decoder.py from gpu_0 to gpu_1 so
test_frames_are_pinned_on_cuda is included in the SGLang GPU job; leave the test
implementation unchanged.
🪄 Autofix

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: 236e2054-91ab-45bb-b492-329f9fc55337

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4fabc and 5df455b.

📒 Files selected for processing (8)
  • components/src/dynamo/frontend/tests/test_sglang_multimodal_prepost.py
  • components/src/dynamo/frontend/tests/test_sglang_processor_api.py
  • components/src/dynamo/frontend/tests/test_sglang_processor_unit.py
  • components/src/dynamo/frontend/tests/test_sglang_tool_calls.py
  • components/src/dynamo/sglang/tests/test_fpm_contract.py
  • components/src/dynamo/sglang/tests/test_sglang_multimodal_embedding_cache.py
  • components/src/dynamo/sglang/tests/test_sglang_nvdec_video_decoder.py
  • components/src/dynamo/sglang/tests/test_sglang_unit.py

Comment thread components/src/dynamo/sglang/tests/test_sglang_nvdec_video_decoder.py Outdated

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Open in Devin Review

Comment thread components/src/dynamo/frontend/tests/test_sglang_processor_unit.py Outdated
@datadog-official

This comment has been minimized.

dmitry-tokarev-nv and others added 2 commits August 5, 2026 17:37
…osts

Verified on the amd64 image with the GPU masked: 14 of the file's tests
fail without a GPU while passing with one, all through the same root --
parse_args reaches SGLang's ServerArgs.__post_init__, which auto-detects
a device when --device is absent, and detection ends in
NotImplementedError on a host with no accelerator (get_device ->
SRTPlatform(unknown).get_device). The arm64 CPU lane never caught this
because the file's old gpu_1 mark kept it off that lane entirely.

An autouse fixture sets SGLANG_USE_CPU_ENGINE=1 (SGLang's public CPU
knob, verified at v0.5.16: is_cpu() reads exactly this env var) only
when CUDA is unavailable, clearing the lru_caches on both probes so
worker-level cache state from other files cannot leak in either
direction. GPU hosts keep exercising the real detection path unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
…sers

Review finding on the gpu_0 reclassification: these files build a real
Qwen tokenizer at module scope, and the move enrolled them in the
amd64/arm64 CPU lanes where nothing warmed the HF cache -- every xdist
worker would fetch it over the network, a flake surface this repo has
been bitten by before. pytest.mark.model() is the declared mechanism:
predownload_tokenizers downloads once per session under a file lock and
the workers reuse the cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
Comment thread components/src/dynamo/frontend/tests/test_sglang_processor_api.py Outdated
Comment thread components/src/dynamo/frontend/tests/test_sglang_processor_unit.py Outdated
Comment thread components/src/dynamo/frontend/tests/test_sglang_processor_unit.py Outdated

@rmccorm4 rmccorm4 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.

frontend test changes LGTM

Comment thread components/src/dynamo/frontend/tests/test_sglang_processor_unit.py
dmitry-tokarev-nv and others added 6 commits August 5, 2026 19:39
…t.py

Co-authored-by: Ryan McCormick <rmccormick@nvidia.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
Co-authored-by: Ryan McCormick <rmccormick@nvidia.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
…t.py

Co-authored-by: Ryan McCormick <rmccormick@nvidia.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
pytest.mark.model does not run any downloader itself. tests/conftest.py
sweeps the marker into the session predownload manifest, and the
tests/frontend gpu_0 modules that request predownload_tokenizers in the
same CI session download that manifest and then flip HF_HUB_OFFLINE for
the remainder of their worker. The marker is what keeps this tokenizer
fetchable on such a worker. Say that, instead of claiming the marker
activates the predownload.

Raised by rmccorm4 on #12718.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>
…nit-tests-gpu0

Signed-off-by: Dmitry Tokarev <dtokarev@nvidia.com>

@Chokoyo Chokoyo 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.

LGTM

@dmitry-tokarev-nv
dmitry-tokarev-nv enabled auto-merge (squash) August 6, 2026 17:59
@nvda-mesharma
nvda-mesharma disabled auto-merge August 6, 2026 20:45
@nvda-mesharma
nvda-mesharma merged commit 8d4a014 into main Aug 6, 2026
7 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend::sglang Relates to the sglang backend frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` size/M test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants