Skip to content

ci: balance fixture-aware test groups#5810

Open
njzjz-bot wants to merge 8 commits into
deepmodeling:masterfrom
njzjz:ci/duration-based-test-split
Open

ci: balance fixture-aware test groups#5810
njzjz-bot wants to merge 8 commits into
deepmodeling:masterfrom
njzjz:ci/duration-based-test-split

Conversation

@njzjz-bot

@njzjz-bot njzjz-bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • group complete test classes and module-level tests into indivisible fixture-sharing units
  • assign units to 12 shards with deterministic longest-processing-time bin packing
  • support @pytest.mark.ci_split_group("name") for merging complete classes/modules that share an expensive fixture; partial method-level markers are rejected
  • use test-count LPT when no grouped duration cache exists, without committing generated historical timing data
  • use the test4 runtime cache after a successful grouped run has produced measured durations
  • record the main, TF2, JAX-to-TF, and consistent-TF2 durations independently, then merge their partial maps without summing duplicate node IDs

Validation

  • 8 splitter unit tests passed on Python 3.10 and Python 3.13
  • two-shard pytest integration runs selected disjoint complete classes and stored all durations
  • malformed legacy cache lists produce a descriptive pytest.UsageError
  • workflow YAML and TF2 helper shell syntax validated
  • ruff check .
  • ruff format .

The previous committed-seed run reduced cumulative main-pytest time by 27.8%, but its slowest shard was 66:15 versus the 57:06 base. Static seed data was therefore removed; the current design relies on measured test4 data for steady-state balancing.

Live CI validation: https://github.com/deepmodeling/deepmd-kit/actions/runs/29687842539

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

Use duration-based contiguous chunks so neighboring tests and their expensive class-scoped setup are less likely to be dispersed across all CI shards.\n\nCoding-Agent: Codex\nCodex-Version: codex-cli 0.144.4\nModel: gpt-5.6-sol\nReasoning-Effort: xhigh
@dosubot dosubot Bot added the build label Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The Python test workflow now invokes a pytest plugin that groups fixture-sharing tests, balances shards using cached durations, and selects a requested CI group. Tests validate grouping and deterministic balancing, while workflow cache keys move to the test4 prefix.

Changes

Python test sharding

Layer / File(s) Summary
Fixture-aware shard allocation
source/tests/ci_split.py
Adds pytest options and hooks that group tests by class, module, or explicit marker, load duration caches, assign units with deterministic LPT balancing, and select the requested shard.
Shard allocation tests
source/tests/test_ci_split.py
Tests indivisible units, cross-module explicit groups, marker validation, cached-duration balancing, empty-cache balancing, and deterministic ordering.
Workflow integration and cache versioning
.github/workflows/test_python.yml
Runs pytest through source.tests.ci_split with --ci-splits and --ci-group, and updates duration cache keys and restore keys to test4.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI as CI workflow
  participant Pytest
  participant Cache as Duration cache
  participant Plugin as ci_split plugin
  CI->>Pytest: Run with shard options
  Pytest->>Plugin: Collect tests and invoke hooks
  Plugin->>Cache: Load persisted durations
  Cache-->>Plugin: Return duration data
  Plugin->>Plugin: Group and balance test units
  Plugin->>Pytest: Select requested shard
Loading
🚥 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: balancing fixture-aware pytest groups for CI sharding.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Start the contiguous splitting algorithm with a clean duration cache so setup costs recorded under least-duration sharding do not distort the new chunk boundaries.\n\nCoding-Agent: Codex\nCodex-Version: codex-cli 0.144.4\nModel: gpt-5.6-sol\nReasoning-Effort: xhigh

@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 @.github/workflows/test_python.yml:
- Line 59: Update the cache key in the test workflow to include
github.run_attempt alongside the existing run identifiers, Python version, and
matrix group, ensuring reruns use distinct per-shard duration caches.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1c7752db-b768-442a-80a3-b068954bf662

📥 Commits

Reviewing files that changed from the base of the PR and between 1377db8 and dd232a6.

📒 Files selected for processing (1)
  • .github/workflows/test_python.yml

Comment thread .github/workflows/test_python.yml Outdated
Include github.run_attempt in each per-shard cache key so workflow reruns cannot restore stale shard-local duration data from an earlier attempt.

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.47%. Comparing base (1c5ff68) to head (49dc028).
⚠️ Report is 17 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5810      +/-   ##
==========================================
- Coverage   78.57%   78.47%   -0.11%     
==========================================
  Files        1049     1053       +4     
  Lines      120650   121050     +400     
  Branches     4348     4382      +34     
==========================================
+ Hits        94806    94988     +182     
- Misses      24285    24514     +229     
+ Partials     1559     1548      -11     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

njzjz-bot and others added 2 commits July 18, 2026 15:31
Keep test classes and module-level tests together, then assign those units with deterministic duration-based LPT bin packing. Warm-start the new cache namespace from existing per-test timings so the first CI run remains balanced.

Coding-Agent: Codex

Codex-Version: codex-cli 0.144.4

Model: gpt-5.6-sol

Reasoning-Effort: xhigh

@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: 3

🤖 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 @.github/workflows/test_python.yml:
- Line 69: Append --store-durations to each TF2 test command in the workflow,
including the commands covering source/tests/tf2, source/jax2tf_tests, and
consistent TF2 tests. Do not add --clean-durations there, so those commands
append their timings to the duration file created by the source/tests step.

In `@source/tests/ci_split.py`:
- Around line 80-89: The explicit-group handling around _explicit_group_name
must preserve complete fixture units: a method-level marker must not separate
one method from the remaining methods in its class. Change grouping so explicit
groups merge the entire class or module unit, or reject markers that do not
cover that unit; add a regression covering a marked class with multiple methods
and verify all methods remain together.
- Around line 162-163: Update the legacy-list normalization in the cache restore
flow around the isinstance(data, list) check to catch TypeError and ValueError
from dict(data), and handle them through the existing cache usage error path.
Ensure malformed restored lists do not escape as unhandled collection failures.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8c61f016-a08f-4964-b0ab-764dacbe312c

📥 Commits

Reviewing files that changed from the base of the PR and between b8b5d0f and f36adb7.

📒 Files selected for processing (3)
  • .github/workflows/test_python.yml
  • source/tests/ci_split.py
  • source/tests/test_ci_split.py

Comment thread .github/workflows/test_python.yml Outdated
Comment thread source/tests/ci_split.py Outdated
Comment thread source/tests/ci_split.py Outdated
@njzjz-bot njzjz-bot changed the title ci: preserve test order across duration shards ci: balance fixture-aware test groups Jul 18, 2026
Comment thread source/tests/ci_split.py Fixed
Use version-controlled Python 3.10 and 3.13 estimates for expensive fixture units instead of relying on a possibly missing test2 cache. Preserve complete class/module units for explicit markers, retain TF2 timings through independent duration files, and report malformed legacy caches as usage errors.

Coding-Agent: Codex

Codex-Version: codex-cli 0.144.4

Model: gpt-5.6-sol

Reasoning-Effort: xhigh
@njzjz-bot

Copy link
Copy Markdown
Contributor Author

CI timing result for the first committed-seed run: https://github.com/deepmodeling/deepmd-kit/actions/runs/29637394120

Group Main pytest TF2 Total job Queue
1 29:27 8:36 40:47 23:39
2 15:13 6:53 25:28 14:53
3 66:15 2:17 71:18 13:03
4 19:11 2:24 24:11 24:25
5 27:19 2:57 33:39 14:23
6 60:58 2:21 66:26 21:01
7 48:29 2:21 53:39 21:43
8 21:01 2:14 26:08 23:22
9 23:05 2:09 28:18 6:01
10 47:25 3:54 54:17 23:37
11 47:35 2:21 53:28 21:24
12 36:09 2:18 42:05 6:19

Compared with the base run (29456447617):

  • cumulative main-pytest time: 10:12:07 -> 7:22:07, 27.8% less runner time
  • cumulative full-job time: 11:31:41 -> 8:39:44, 24.9% less runner time
  • slowest main-pytest shard: 57:06 -> 66:15, 16.0% slower
  • slowest full job: 68:18 -> 71:18, 4.4% slower
  • workflow wall time: 1:13:21 -> 1:29:30, 22.0% slower, including runner queueing

Compared with the previous duration_based_chunks run (29513326076), the slowest main shard improved from 85:17 to 66:15 (22.3%), although cumulative main-pytest time increased by 5.0%.

Conclusion: this run reduced duplicated setup and total runner-minutes, but did not accelerate the CI critical path. The first-run seed remained imbalanced, especially groups 3 and 6. A second run using the newly generated test4 grouped-duration cache is needed to evaluate steady-state performance.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

Use test-count LPT when no grouped runtime cache exists and keep test4 as the only persisted duration source. Retain regression coverage for the custom splitter while removing generated historical timing data.

Coding-Agent: Codex

Codex-Version: codex-cli 0.144.4

Model: gpt-5.6-sol

Reasoning-Effort: xhigh
@njzjz-bot

Copy link
Copy Markdown
Contributor Author

Final timing result; leaving the implementation as-is without further changes.

Latest run: https://github.com/deepmodeling/deepmd-kit/actions/runs/29687842539

Group Main pytest TF2 Total job
1 14:55 4:38 22:03
2 2:36 5:48 11:02
3 3:49 8:56 15:30
4 4:47 3:11 11:37
5 62:28 2:53 68:04
6 52:48 2:27 58:07
7 51:56 2:28 57:29
8 58:59 2:32 64:35
9 56:17 2:36 61:53
10 56:12 2:21 61:35
11 54:59 3:41 61:44
12 30:27 1:31 35:25

Compared with the base run (29456447617):

  • cumulative main-pytest time: 10:12:07 -> 7:30:13, 26.4% less runner time
  • cumulative full-job time: 11:31:41 -> 8:49:04, 23.5% less runner time
  • slowest main-pytest shard: 57:06 -> 62:28, 9.4% slower
  • slowest full job: 68:18 -> 68:04, 14 seconds faster and effectively unchanged

Compared with the preceding committed-seed run (29637394120):

  • slowest main-pytest shard: 66:15 -> 62:28, 5.7% faster
  • slowest full job: 71:18 -> 68:04, 4.5% faster
  • cumulative runner time increased by about 1.8%
  • workflow wall time improved from 1:29:30 to 1:08:25, primarily because runner queueing disappeared

Conclusion: the implementation saves approximately 24-26% of total runner usage, while the CI critical path is effectively unchanged from the base. The shard allocation remains uneven, with groups 1-4 much shorter than groups 5-11, but no further changes are planned for this PR.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

Coding-Agent: Codex
Codex-Version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning-Effort: xhigh
@njzjz-bot

njzjz-bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Warm-cache rerun results: https://github.com/deepmodeling/deepmd-kit/actions/runs/29692580279

All 12 Python 3.10 shards passed.

The primary comparison below is the nearly concurrent successful master run: https://github.com/deepmodeling/deepmd-kit/actions/runs/29692274644

This is a like-for-like base comparison: the master run tested 5a1bbf9d, and the PR run tested merge commit c6e13c4b, whose first parent is exactly 5a1bbf9d. The master run started at 15:09 UTC and the PR run at 15:18 UTC.

Group Master main pytest PR main pytest Master full job PR full job
1 56:44 15:12 1:06:29 27:47
2 1:14:48 2:35 1:26:00 11:34
3 58:45 2:59 1:05:22 10:34
4 59:58 4:02 1:05:46 8:59
5 53:53 58:29 58:41 1:04:00
6 56:22 42:18 1:01:27 47:04
7 1:03:28 52:19 1:09:08 57:20
8 56:35 51:45 1:01:42 57:18
9 1:07:51 54:32 1:13:38 59:47
10 1:05:50 57:02 1:12:47 1:02:18
11 1:04:01 54:01 1:09:31 1:00:45
12 1:07:13 45:26 1:12:28 50:14

The group numbers are not semantically equivalent because master splits individual tests while the PR splits fixture-sharing units, so the aggregate and critical-path comparisons are the meaningful results:

  • Cumulative main pytest time: 12:25:28 -> 7:20:40, 40.9% lower, saving 5:04:48 of runner time.
  • Cumulative full-job time: 13:42:59 -> 8:37:40, 37.1% lower, saving 5:05:19 of runner time.
  • Slowest main pytest: 1:14:48 -> 58:29, 21.8% faster, saving 16:19 on the main-test critical path.
  • Slowest full job: 1:26:00 -> 1:04:00, 25.6% faster, saving 22:00 on the full-job critical path.

For the warm-cache-specific comparison against the preceding PR run, cumulative main/full-job time improved by another 2.1%/2.2%, while the slowest main/full job improved by 6.4%/6.0%.

The warm cache successfully redistributed ordinary stable units. However, it still does not match durations for the four generated single-unit shards in groups 1-4. Their pytest nodeids contain process-specific memory addresses such as ... at 0x7fdf...; because the cache is keyed by the complete nodeid, those entries miss on the next process and fall back to average-duration times test-count estimates. This explains why their estimates remain much larger than their actual runtime.

Conclusion: compared with current master, fixture-aware grouping materially reduces both total runner use and the CI critical path. Warm-cache reuse gives a smaller additional improvement, but the volatile-nodeid issue remains if we want the four large units to be estimated correctly.

Coding agent: Codex
Codex version: codex-cli 0.144.4
Model: gpt-5.6-sol
Reasoning effort: xhigh

@njzjz
njzjz requested a review from wanghan-iapcm July 19, 2026 16:55
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.

3 participants