Skip to content

[OMNIML-6179] [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data - #2850

Open
shengliangxu wants to merge 5 commits into
shengliangx/ci-5-onnx-testsfrom
shengliangx/ci-6-local-data-tests
Open

shengliangxu wants to merge 5 commits into
shengliangx/ci-5-onnx-testsfrom
shengliangx/ci-6-local-data-tests

Conversation

@shengliangxu

@shengliangxu shengliangxu commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Type of change: CI (test runtime and reliability, tests only)

Less data and fewer Hub round trips in four GPU-lane tests:

  • Six preprocess_data tests use the tiny local tokenizer (tests/gpu_megatron). They tokenized with gpt2 from the Hub, and megatron_preprocess_data loads the tokenizer once for the vocabulary size and again in every worker, so each test made several round trips; none depends on gpt2's vocabulary. They use the checked-in byte-level tiny_tokenizer_path fixture that the blend test already uses. Three of the six read only local data, so the tokenizer download was their only network access (the jsonl max_tokens test is among the lane's 120 s timeouts, 3 failures). Measured on a CPU host with a cold Hub cache, the 11 tests of the module take 42.8 and 44.0 s before and 35.0 and 36.1 s after (two runs each; the nine tests of the earlier base took 38.5 s and 24.4 s, and 37 s and 23 s in CI). The Qwen3 tests keep their real tokenizer (chat template and EOS handling).
  • The puzzletron test data shrinks, and a duplicate NAS test is folded into another. save_dummy_dataset wrote 2500 copies of one conversation into each split (1738 blocks of 2048 tokens), while the pruning scores read 100 blocks and the later steps 2 each; 400 conversations (278 blocks) take the three dataloader builds plus the save from 2.8 s to 0.36 s per test, and the validation loss moved by at most 0.02 on random-init toy Llamas (the scoring tolerance is 0.15). calc_subblock_stats ran 12 passes where only the entry for batch_size 96 is read: batch_sizes: [96] runs 4. test_nas_convert_ffn_pruning ran the same mtn.convert on the same toy Llama and config as test_nas_search (21.6 s with its own 2-rank spawn); its two assertions move into test_nas_search.
  • The unified HF export test calibrates on local text, with no previews or batch probing. Each of its 11 params starts a fresh hf_ptq process (22 s per param). It now reads a local jsonl of 64 random-word texts (new helper write_calib_jsonl, shared through the session-wide calib_jsonl fixture in tests/gpu/conftest.py, which fails the session if a test writes into its directory) instead of cnn_dailymail from the Hub, passes --batch_size 64 (the whole calibration set in one batch instead of probing for the largest batch) and --skip_generate. The test only checks the structure of the exported safetensors.
  • The GPTQ end-to-end test reads 8 texts from the same local jsonl instead of streaming cnn_dailymail for each of its three params (and a test that could fail on a Hub hiccup).

Time improvement (measured in CI)

Time of the changed tests; before = the runs of #2846-#2849, after = #2850, #2852, #2854 and #2855 (median). #2849 and #2850 ran on runners 1.26x and 1.29x slower for every test, so the change after speed scaling is in brackets.

lane tests before after change
gpu tests/gpu/torch/puzzletron (18 tests, 17 after the folded NAS test) 290 s 202 s -88 s (-70 s, -26%)
gpu unified HF export, 11 params 228 s 153 s -75 s (-67 s, -30%)
gpu GPTQ end-to-end, 3 params 7.2 s 1.1 s -6 s
gpu_megatron preprocess_data module 37 s 23 s -14 s (-10 s, -27%)

So about 170 s (144 s after scaling) off the gpu job and 14 s off the gpu_megatron job. This PR's own run was on a slow runner (puzzletron 310 s); the three later runs agree with each other (199-203 s). My first estimates were 50 s, 38 s and 6 s for the first three rows.

Method: every PR of the series has its own CI run, so "before" is the run of the PR(s) below that lack the change and "after" the runs that carry it. Times come from the job logs (--durations, or the gap between consecutive result lines). Where runner speed matters, the tests that no PR of the series touches scale each run, and the raw and the scaled medians are both shown. One test varies by 15-30% from run to run on its own, so smaller differences are not evidence.

Usage

Nothing user-facing.

Testing

  • preprocess_data: ran the module on CPU next to a copy of the tiny_tokenizer_path fixture with the Hub reachable: 11 passed before and after (main has dropped the HF xfail marks), with identical test ids.
  • calib_jsonl: with the real fixture and the repo's get_dataset_dataloader on a CPU host, the 8 samples read from the shared 64-text file equal those of the old 8-text file, and two reads leave the directory unmodified (the fixture's teardown check passes). --setup-plan shows the unified export test using the conftest fixture; the GPTQ and export tests themselves need a GPU.
  • puzzletron: timed the dataloader builds, the subblock-stats passes and the loss sensitivity on CPU with the repo's own helpers; collected tests/gpu/torch/puzzletron (16 tests, one fewer than before). The Nemotron-3 toy keeps its 128 experts, because shrinking it changes EXPECTED_TEACHER_MEMORY_MIB and EXPECTED_TEACHER_NUM_PARAMS, which need a GPU runner with mamba_ssm to regenerate.
  • unified export: no GPU here, so I ran the test on a CPU host through a shim that lets hf_ptq pass its GPU guard and passes --device cpu. 8 of the 11 params pass with the new command line, scale-equality assertions included (fp8, nvfp4, int4_awq, w4a8_awq_beta, int8_weight_only, w4a16_nvfp4, the Qwen3 MoE nvfp4 and the GPT-OSS fp8); the other three need CUDA or Triton. GPTQ: the flow completes on the jsonl for the FP8 and INT4 blockwise configs (NVFP4 needs CUDA).
  • Not run: the GPU tests themselves; this PR's gpu and gpu_megatron jobs are the check. Pre-commit passes.

Before your PR is "Ready for review"

Make sure you read and follow Contributor guidelines and your commits are signed (git commit -s -S).

Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded trust_remote_code=True, torch.load(..., weights_only=False), pickle, etc.).

  • Is this change backward compatible?: N/A (CI and tests)
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: no new dependency
  • Did you write any new necessary tests?: ✅ one small helper, write_calib_jsonl (tests/_test_utils/torch/calib_data.py), and one session fixture, calib_jsonl (tests/gpu/conftest.py), used by two tests; no new test functions
  • Did you update Changelog?: N/A (CI and tests)
  • Did you get Claude approval on this PR?: N/A (optional, not run)

Additional Information

Slice 6 of 11 of the GPU-CI speed-up work, split out of the reference draft #2730 (not for merge; it holds the whole series with its measurements). Merge in order; each PR stands on its own and passes CI without the later ones.

  1. [OMNIML-6178] [1/11] Run the unit tests in parallel with pytest-xdist #2845 [OMNIML-6178] [1/11] Run the unit tests in parallel with pytest-xdist
  2. [OMNIML-6178] [2/11] Use the sysmon coverage core in CI #2846 [OMNIML-6178] [2/11] Use the sysmon coverage core in CI
  3. [OMNIML-6179] [3/11] Run manual-marked tests on nightly and on-demand GPU runs #2847 [OMNIML-6179] [3/11] Run manual-marked tests on nightly and on-demand GPU runs
  4. [OMNIML-6181] [4/11] Give gpu_megatron's slow cold starts more time #2848 [OMNIML-6181] [4/11] Give gpu_megatron's slow cold starts more time
  5. [OMNIML-6179] [5/11] Make the ONNX tests cheaper: CPU-only ones to the unit lane, small models #2849 [OMNIML-6179] [5/11] Make the ONNX tests cheaper: CPU-only ones to the unit lane, small models
  6. [OMNIML-6179] [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data #2850 [OMNIML-6179] [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data (this PR)
  7. [OMNIML-6180] [7/11] Trim the llm_eval, llm_qat, EAGLE and megatron_bridge example tests #2851 [OMNIML-6180] [7/11] Trim the llm_eval, llm_qat, EAGLE and megatron_bridge example tests
  8. [OMNIML-6180] [8/11] Trim the DFlash regression test #2852 [OMNIML-6180] [8/11] Trim the DFlash regression test
  9. [OMNIML-6180] [9/11] Trim the torch_onnx example lane #2853 [OMNIML-6180] [9/11] Trim the torch_onnx example lane
  10. [OMNIML-6181] [10/11] Make the distributed worker pool fail fast when a worker dies #2854 [OMNIML-6181] [10/11] Make the distributed worker pool fail fast when a worker dies
  11. [OMNIML-6181] [11/11] Share one Megatron worker pool across the gpu_megatron modules #2855 [OMNIML-6181] [11/11] Share one Megatron worker pool across the gpu_megatron modules

Summary by CodeRabbit

  • Tests
    • Updated model export and quantization checks to use locally generated calibration data instead of a public dataset.
    • Standardized preprocessing tests on a small local tokenizer.
    • Restricted model analysis test configurations to a batch size of 96.
    • Reduced the size of sample datasets used by pruning-related checks.
    • Strengthened conversion verification by checking generated pruning outputs.
    • Removed the FFN pruning conversion test while retaining attention-pruning coverage.

@copy-pr-bot

copy-pr-bot Bot commented Oct 17, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/Model-Optimizer/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 5af47c9f-7c1b-4c6b-99dc-f4e2aa3e6be4
📥 Commits

Reviewing files that changed from the base of the PR and between dba99af and bd0525c.

📒 Files selected for processing: 2
  • tests/gpu/conftest.py
  • tests/gpu/torch/quantization/test_gptq.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 remain after this review.

📝 Walkthrough

The changes add deterministic local calibration data, adjust Puzzletron workloads and NAS assertions, and replace hard-coded tokenizer and dataset identifiers in test fixtures.

Changes: Local calibration data

Layer / File(s) Summary
Generate and use local calibration data
tests/_test_utils/torch/calib_data.py, tests/gpu/conftest.py, tests/gpu/torch/export/..., tests/gpu/torch/quantization/test_gptq.py
A deterministic JSONL calibration-data helper and fixture are added. The export and GPTQ tests use the generated file instead of cnn_dailymail. The export test sets batch size to 64 and adds --skip_generate.

Puzzletron test workloads

Layer / File(s) Summary
Update Puzzletron workload and NAS tests
tests/_test_utils/torch/puzzletron/utils.py, tests/gpu/torch/puzzletron/resources/configs/..., tests/gpu/torch/puzzletron/test_nas_convert.py, tests/gpu/torch/puzzletron/test_nas_search.py
The dummy dataset repetition count changes from 2,500 to 400. Listed calc_subblock_stats.batch_sizes values are restricted to [96]. The FFN-pruning conversion test is removed, and the NAS search test checks conversion outputs.

Megatron preprocessing tests

Layer / File(s) Summary
Use the tiny tokenizer fixture
tests/gpu_megatron/torch/utils/plugins/test_megatron_preprocess_data.py
JSONL, Hugging Face, cached-resume, gzip, and streaming-warning tests use tiny_tokenizer_path instead of "gpt2".

Priority: ⬇️ Low

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

Change: Other

Suggested reviewers: kevalmorabia97

Merge Risk: ⚪ Minimal · up to bd052

No concrete merge-blocking issue is established; the changes are mergeable with normal CI validation.

Pre-merge checks: Passed 5 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely summarizes the main changes: using local tokenizers and calibration text in GPU tests and reducing Puzzletron test data.
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.
Security Anti-Patterns Passed PASS. The pull request changes only test helpers, GPU test files, and YAML configurations. It adds no Python changes under modelopt/ or examples/, and it changes no pyproject.toml or `requiremen…
Full details: Security Anti-Patterns

Explanation

PASS. The pull request changes only test helpers, GPU test files, and YAML configurations. It adds no Python changes under modelopt/ or examples/, and it changes no pyproject.toml or requirements*.txt files. Therefore, none of the specified security anti-patterns is introduced in the reviewed scope.

✨ Finishing Touches: 💡 2
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@shengliangxu

Copy link
Copy Markdown
Collaborator Author

/ok to test 2d82a59

@codecov

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.90%. Comparing base (2d43ca2) to head (bd0525c).

Additional details and impacted files
@@                       Coverage Diff                       @@
##           shengliangx/ci-5-onnx-tests    #2850      +/-   ##
===============================================================
- Coverage                        76.90%   76.90%   -0.01%     
===============================================================
  Files                              658      658              
  Lines                            74029    74029              
===============================================================
- Hits                             56933    56932       -1     
- Misses                           17096    17097       +1     
Flag Coverage Δ
unit 60.54% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@shengliangxu shengliangxu changed the title [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data [6/12] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data Oct 17, 2026
@shengliangxu
shengliangxu removed this pull request from stack #2856 October 17, 2026 06:58
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch from 2d82a59 to 7383ca1 Compare October 17, 2026 07:02
@shengliangxu
shengliangxu added this pull request to stack #2859 October 17, 2026 07:02
@shengliangxu shengliangxu changed the title [6/12] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data Oct 17, 2026
@shengliangxu

Copy link
Copy Markdown
Collaborator Author

/ok to test 7383ca1

@shengliangxu shengliangxu changed the title [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data [OMNIML-6179] [6/11] Use local tokenizers and calibration text in GPU tests, shrink puzzletron data Oct 18, 2026
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch from 7383ca1 to 5f3497e Compare October 18, 2026 23:15
@shengliangxu
shengliangxu marked this pull request as ready for review October 19, 2026 03:44
@shengliangxu
shengliangxu requested a review from a team as a code owner October 19, 2026 03:44
from safetensors import safe_open


@pytest.fixture(scope="module")

@kevalmorabia97 kevalmorabia97 Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you make this session wide fixture in tests/{gpu,gpu_megatron}/conftest.py and reuse in all tests since we dont expect calibration data to be modified by a test (can make this directory read only to restrict any tests modifying it)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done in 546bff3: calib_jsonl is a session-wide fixture in tests/gpu/conftest.py (the 64 texts are written once). I used assert_unmodified_tree, the check the shared tiny-tokenizer fixture already uses, instead of chmod: the CI containers run as root, so a read-only directory would not stop a write (the helper's docstring has the details); a test that writes into it now fails at teardown. The unified HF export test and the GPTQ end-to-end test both use it. The GPTQ test still asks the dataloader for 8 samples, and the shared file starts with the same 8 texts as before.

I did not add a copy to tests/gpu_megatron/conftest.py: none of its tests calibrates on text (they use model.get_input()), so it would be unused. If one needs it later, the fixture can move up to tests/conftest.py.

@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch from 5f3497e to 1b417b8 Compare October 19, 2026 21:34
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch 2 times, most recently from 413e380 to dba99af Compare October 20, 2026 18:40
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch from dba99af to cbda9e7 Compare October 20, 2026 18:58
shengliangxu added a commit that referenced this pull request Oct 20, 2026
…#2845)

### What does this PR do?

Type of change: CI (unit-test runtime)

The unit `linux` job gates every GPU and example workflow (their pr-gate
jobs wait for it), and its tests are CPU-bound but run serially. This
runs them under pytest-xdist on the runner's four vCPUs. The `linux` job
and the multi-version matrix get `PYTEST_ADDOPTS=-n 4 --dist loadfile`
and `OMP_NUM_THREADS=1`: one worker per vCPU (`-n auto` counts physical
cores), whole files per worker, and one thread per worker, because with
torch's default threads the runner is oversubscribed and slower than
serial (1,481 s and 41 failures in my local runs).

What else changes so the suite passes under xdist:

- `noxfile.py`: the second pytest process that runs the `manual` tests
clears `PYTEST_ADDOPTS`, so it stays serial. Those tests leak global
state into each other, and a worker would run them in some order.
- `tests/_test_utils/torch/quantization/onnx_export.py`: `TEST_MODELS`
was a set of classes, whose iteration order differs between processes,
so workers collected different test ids. It is a tuple now.
- `test_hf_dflash2.py::test_overfits_a_single_batch` takes about 30 s on
one thread on main (53 s before #2836 halved its loop to 30 steps; that
version hit the 60 s default call timeout in the nightly on main on
2026-10-20). Its tiny model is built in bf16, and a CPU without native
bf16 support emulates bf16 matmuls (about 0.9 s per step, 0.1 s in
float32). The model is float32 now, which takes the test to about 4 s on
one thread, so it needs no timeout override; the loop and both
assertions are unchanged.
- `test_full_algorithms_vision.py::test_search_constraints` assigned
`ConstraintsFunc._get_true_latency` and never restored it. Serial runs
never noticed, because `test_evaluate_constraints.py` sorts first. Under
xdist a worker can get the files in the other order, and the Python 3.10
and 3.14 legs failed two tests after it. It uses `monkeypatch` now.
- `tests/conftest.py`: the unit per-test cap is 120 s on every platform.
`test_dist.py::test_data_parallel` takes 8-15 s serial and 25-35 s with
four workers sharing four vCPUs, and went over 60 s on the slowest
runner. A hung test is still reported, after 2 min instead of 1.

Why: on current main the `linux` job has a median of 12.9 min (27 runs
of other PRs since #2823 merged), and a pull request's GPU run waits a
median 14.5 min for it (125 runs of 33 PRs).

### Time improvement (measured in CI)

Unit `linux` job: serial on current main (27 runs of other PRs since
#2823 merged) against the runs of this series with `-n 4`.

| | before (serial) | with this PR |
|---|---|---|
| pytest | 630 s median (355-709 s) | 479 s in this PR's own run; 294 s
median over the 11 runs of the series (166-479 s) |
| whole job | 12.9 min median (8.8-14.8) | 10.9 min in this PR's own
run; 7.0 min median over the 11 runs (5.0-10.9) |
| unit workflow: `linux`, then the multi-version and Windows legs | 24.1
min median (12 runs) | 17.2 min median (the 7 runs that ran every leg) |

- The runs of #2846 and above also carry the sysmon core of #2846 (about
22% off the unit suite on its own, measured locally), so the 294 s
median is xdist plus sysmon; this PR's own run is the xdist-only number.
It was the slowest of the 11 (166-479 s for nearly the same tests:
runner variance), so it understates the gain. Locally on 4 CPUs with
coverage as in CI, the base took 825 s serial and 332-344 s with this
configuration (3 runs).
- The GPU, Example and Regression workflows hold their jobs until the
unit `linux` check is done (`pr-gate / wait-checks`, at least 5 min).
That wait is a median 14.5 min (IQR 12.9-15.8) on 125 GPU runs of 33 PRs
since #2823; in this PR's own GPU run it was 9.7 min (its `linux` job
took 10.9 min). With a 7 min `linux` job instead of 12.9 min, a GPU run
requested right after a push starts about 6 min earlier. That figure is
derived from the job times: the later runs of the series were requested
after their unit check had finished (the wait sat at its 5.2 min floor
in 7 of the 9 GPU runs), so they cannot show it.

Method: every PR of the series has its own CI run, so "before" is the
run of the PR(s) below that lack the change and "after" the runs that
carry it. Times come from the job logs (`--durations`, or the gap
between consecutive result lines). Where runner speed matters, the tests
that no PR of the series touches scale each run, and the raw and the
scaled medians are both shown. One test varies by 15-30% from run to run
on its own, so smaller differences are not evidence.

### Usage

Nothing user-facing. Locally: `OMP_NUM_THREADS=1 pytest tests/unit -n 4
--dist loadfile`.

### Testing

- Full `tests/unit` on 4 CPUs with coverage as in CI, on the base before
#2823: serial 825 s, this configuration 332-344 s in three repeated runs
(4,724 passed, 0 failed).
- This PR's own CI run: the `linux` job took 10.9 min (479 s of pytest)
against the 12.9 min serial median; see the time section above for the
later runs of the series.
- `tests/unit` without `puzzletron` (it needs `hydra`, absent from my
local environment) in reversed and in shuffled file order shows no other
order dependence; `test_search_constraints` followed by
`test_evaluate_constraints.py` in one process failed before the fix and
passes after it.
- `test_hf_dflash2.py` (33 tests) on one CPU thread: 13.5 s, of which
`test_overfits_a_single_batch` takes about 4 s (30 s with the bf16 model
on main; the next slowest test takes about 1.3 s).
- Pre-commit (all hooks) passes.

### Before your PR is "*Ready for review*"

Make sure you read and follow [Contributor
guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md)
and your commits are signed (`git commit -s -S`).

Make sure you read and follow the [Security Best
Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors)
(e.g. avoiding hardcoded `trust_remote_code=True`, `torch.load(...,
weights_only=False)`, `pickle`, etc.).

- Is this change backward compatible?: N/A (CI and tests)
- If you copied code from any other sources or added a new PIP
dependency, did you follow guidance in `CONTRIBUTING.md`: no new
dependency (`pytest-xdist` is already in the `dev-test` extra on main)
- Did you write any new necessary tests?: N/A (two existing tests fixed
or sped up so they pass under xdist)
- Did you update
[Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?:
N/A (CI and tests)
- Did you get Claude approval on this PR?: N/A (optional, not run)

### Additional Information

Slice 1 of 11 of the GPU-CI speed-up work, split out of the reference
draft #2730 (not for merge; it holds the whole series with its
measurements). Merge in order; each PR stands on its own and passes CI
without the later ones.

1. #2845 [OMNIML-6178] [1/11] Run the unit tests in parallel with
pytest-xdist **(this PR)**
2. #2846 [OMNIML-6178] [2/11] Use the sysmon coverage core in CI
3. #2847 [OMNIML-6179] [3/11] Run manual-marked tests on nightly and
on-demand GPU runs
4. #2848 [OMNIML-6181] [4/11] Give gpu_megatron's slow cold starts more
time
5. #2849 [OMNIML-6179] [5/11] Make the ONNX tests cheaper: CPU-only ones
to the unit lane, small models
6. #2850 [OMNIML-6179] [6/11] Use local tokenizers and calibration text
in GPU tests, shrink puzzletron data
7. #2851 [OMNIML-6180] [7/11] Trim the llm_eval, llm_qat, EAGLE and
megatron_bridge example tests
8. #2852 [OMNIML-6180] [8/11] Trim the DFlash regression test
9. #2853 [OMNIML-6180] [9/11] Trim the torch_onnx example lane
10. #2854 [OMNIML-6181] [10/11] Make the distributed worker pool fail
fast when a worker dies
11. #2855 [OMNIML-6181] [11/11] Share one Megatron worker pool across
the gpu_megatron modules


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Improved consistency and performance of automated test runs across
platforms and Python configurations through controlled parallel
execution.
* Preserved predictable model-check ordering and isolated temporary test
changes.
* Increased unit-test time limits to better support process-spawning and
slower runs.
* Improved test reliability by explicitly controlling numeric precision
and ensuring serial execution for manual CPU tests.
  * No end-user functionality or product behavior changed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Six tests in test_megatron_preprocess_data.py tokenize with gpt2 from the Hugging Face
Hub. megatron_preprocess_data loads the tokenizer once for the vocabulary size and again
in every worker, so each test makes several hub round trips, and none of them depends on
gpt2's vocabulary: they check output naming, non-empty .bin and .idx files, and that
max_tokens shortens the output. Switch them to the tiny_tokenizer_path fixture, the
checked-in byte-level tokenizer that the blend test already uses.

Three of the six read only local data (the jsonl path, the jsonl max_tokens and the gzip
tests), so the tokenizer download was their only network access. The PR history lists the
jsonl max_tokens test, with the HF split max_tokens test, among the lane's 120 s timeouts
(3 failures each); it no longer has a network dependency to time out on. The other three
(HF split max_tokens, HF resume and the HF streaming warning) still read the real HF
dataset, which is the code path they exist to cover; only their tokenizer changes.

The Qwen3 tests keep their real tokenizer: they need its chat template and EOS handling.
The blend test already uses the tiny tokenizer, and its HF split source is the path it
covers, so it is unchanged. No test ids, assertions or markers change.

Measured on a CPU host with a cold Hugging Face cache, the module's 11 tests take 42.8 and
44.0 s before and 35.0 and 36.1 s after (two runs each, with the conftest's forkserver
prestart). Per test: jsonl path 2.7-3.4 s to 0.01 s, gzip 1.4 s to 0.05 s, HF split
max_tokens 5.8-5.9 s to 4.3 s, HF resume 4.4 s to 2.8-3.0 s, HF streaming warning 3.6-3.8 s
to 2.3-2.7 s. The jsonl max_tokens test takes about 14 s in both, because here it is the
first test to wait for the forkserver's cold import; in the lane the session fixture
starts that import earlier. CI jobs start without a warm hub cache, so a similar saving
is expected.

Testing: ruff check and ruff format with the pinned ruff 0.15.20, and py_compile. Ran the
module on CPU, next to a copy of the tiny_tokenizer_path fixture and the forkserver
prestart from tests/gpu_megatron/conftest.py, with the Hub reachable: 11 passed before and
after, and the collected test ids are identical. Not run: the gpu_megatron lane itself,
which needs GPUs and prebuilds CUDA extensions in its conftest.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…other

- save_dummy_dataset wrote 2500 copies of one conversation into each of the
  train and valid splits. The tiny test tokenizer turns a conversation into
  1424 tokens, so that is 1738 blocks of 2048 tokens, while the pruning
  scores read 100 blocks and the scoring and realize steps 2 each. Every
  dataloader build tokenizes the whole split, and a run builds three of
  them. The 2500 came from an older 8192-token Llama-3 setup. Use 400
  conversations (278 blocks). On a CPU the three builds plus the dataset
  save took 2.8 s per test with 2500 conversations and 0.36 s with 400. The
  scoring step checks lm_loss with a tolerance of 0.15; on random-init toy
  Llamas the validation loss moved by at most 0.02 between the two sizes,
  because the same text repeats and only the choice of blocks changes.
- calc_subblock_stats ran 12 passes (batch sizes 64, 96 and 128 times four
  dtype sets), but the MIP and the teacher memory and parameter checks only
  read the entry for the batch_size in mip.subblock_stats_args, which is 96
  in all nine configs. batch_sizes: [96] gives 4 passes (0.56 s to 0.18 s
  for a toy Llama on a CPU; MoE configs have more subblocks per pass).
- test_nas_convert_ffn_pruning ran the same mtn.convert call on the same toy
  Llama and config as test_nas_search (21.6 s in the CI logs, with its own
  2-rank spawn). Its two assertions move into test_nas_search, which checks
  them with the other rank-0 assertions after the search.

Left alone: the Nemotron-3 toy keeps its 128 experts, because shrinking it
changes EXPECTED_TEACHER_MEMORY_MIB and EXPECTED_TEACHER_NUM_PARAMS, which
need a GPU runner with mamba_ssm to regenerate.

Testing: timed the dataloader builds, the subblock-stats passes and the
loss sensitivity on CPU with the repository's own helpers and dataloader
code, collected tests/gpu/torch/puzzletron (16 tests, one fewer than
before), and ran ruff. Not run: the puzzletron tests themselves, which need
GPUs and mamba_ssm. Expect the gpu job to lose roughly 50 s (the removed
test is 22 s of it); the CI run will show the real number.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…or batch probing

Each of the 11 test_unified_hf_export_and_check_safetensors params starts a
fresh hf_ptq process, 22 s per param in the CI logs. The test only checks the
structure of the exported safetensors, so three parts of that run are not
needed:

- Calibration text came from cnn_dailymail on the Hub, a network round trip
  per param. Use a local jsonl of 64 random-word texts instead, written by a
  new helper, write_calib_jsonl, in _test_utils/torch/calib_data.py. Each text
  fills the 512-token calibration window and differs from the others, so the
  activation statistics stay varied.
- hf_ptq probed for the largest calibration batch. --batch_size 64 puts the
  whole calibration set (calib_size 64) in one batch.
- hf_ptq ran model.generate() before and after quantization. --skip_generate
  skips those previews; the export is not affected.

Testing: no GPU here, so I ran the test on a CPU host through a python shim
that lets hf_ptq pass its GPU guard and passes --device cpu. 8 of the 11
params pass with the new command line, scale-equality assertions included:
fp8, nvfp4, int4_awq, w4a8_awq_beta, int8_weight_only, w4a16_nvfp4, the
Qwen3 MoE nvfp4 and the GPT-OSS fp8. The other three (nvfp4 AWQ-lite,
SVDQuant and the MSE sweep) need CUDA or Triton and did not run. The
dataloader returns one (64, 512) batch with a full attention mask from the
helper's file. Not run: the GPU job itself. From the per-param CI timings
this should save about 38 s per `gpu` job.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
test_gptq_e2e_flow streamed cnn_dailymail from the Hub for each of its three params, a
network round trip per param (about 6 s per job in the CI logs) and a test that can fail
on a Hub hiccup. The GPTQ calibration only needs some text, and the test has no accuracy
assertion, so it now reads 8 texts from the local jsonl that write_calib_jsonl (added with
the unified-export change) creates.

Testing: ruff check and format with the pinned ruff 0.15.20. On a CPU host the GPTQ flow
completes on that jsonl for the FP8 and INT4 blockwise configs; the NVFP4 config needs
CUDA. Not run: the GPU test itself. Expected saving is a few seconds per gpu job.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Review feedback on this PR: the calibration texts are never modified by a test, so one
session-wide fixture can serve every test that needs some text instead of each test or module
writing its own file.

`calib_jsonl` in tests/gpu/conftest.py writes the 64 texts once per session and wraps the
directory in assert_unmodified_tree, the check the shared tiny-tokenizer fixture already uses
(chmod would not stop a root CI container). The unified HF export test drops its module-scoped
fixture of the same name, and the GPTQ end-to-end test takes the fixture instead of writing 8
texts into its tmp_path. It still asks the dataloader for 8 samples, and the file starts with
the same 8 texts the old 8-text file held, since the generator is seeded and sequential.

tests/gpu_megatron has no test that calibrates on text (its tests use model.get_input()), so
it gets no copy of the fixture.

Testing: pre-commit passes. On a CPU host, with the real fixture and the repo's
get_dataset_dataloader: the 8 samples read from the shared file equal those of the old 8-text
file, and two reads leave the directory unmodified (the fixture's teardown check passes).
`--setup-plan` shows the unified export test using the conftest fixture. Not run: the GPTQ
and export tests themselves, which need a GPU.

Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
@shengliangxu
shengliangxu force-pushed the shengliangx/ci-6-local-data-tests branch from 546bff3 to bd0525c Compare October 20, 2026 19:43

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants