Skip to content

Prevent CUDA error cascade on multi-GPU test runs - #2234

Merged
adenzler-nvidia merged 4 commits into
newton-physics:mainfrom
shi-eric:ershi/fix-unittest-error-cascade
Apr 24, 2026
Merged

adenzler-nvidia merged 4 commits into
newton-physics:mainfrom
shi-eric:ershi/fix-unittest-error-cascade

Conversation

@shi-eric

@shi-eric shi-eric commented Mar 25, 2026 •

Copy link
Copy Markdown
Member

Description

Prevent CUDA errors from cascading across unrelated test suites in the parallel test runner on multi-GPU systems.

Without process isolation, a fatal CUDA error in one test suite (e.g. an illegal memory access) corrupts the CUDA context for the entire worker process. Every subsequent test suite dispatched to that worker then fails — even completely unrelated tests — because the context is unrecoverable.

This PR sets max_tasks_per_child=1 on the ProcessPoolExecutor (Python 3.11+) so each worker is replaced after completing a single test suite, but only when multiple CUDA devices are detected or when --disable-process-pooling is explicitly passed. Single-GPU systems retain process reuse for faster throughput.

Checklist

  • New or existing tests cover these changes
  • The documentation is up to date with these changes
  • CHANGELOG.md has been updated (if user-facing change)

Test plan

All multi-GPU tests pass on main today. To verify this change prevents error cascade, we temporarily reverted #2226 (which fixed the original test_sdf_texture CUDA errors) to reproduce the cascading failure scenario on a multi-GPU AWS EC2 instance (3247 tests).

Without process isolation: 7 errors
An illegal memory access in test_sdf_texture cascaded to 4 unrelated tests across different modules:

Test Module Error
test_uint16_vs_nanovdb_distance_cuda_1 test_sdf_texture CUDA error 700 (root cause)
test_uint16_vs_nanovdb_gradient_cuda_0 test_sdf_texture Failed to allocate 12 bytes
test_uint16_vs_nanovdb_gradient_cuda_1 test_sdf_texture Failed to allocate 12 bytes
test_capsule_geometry_is_batched_across_scales test_viewer_geometry_batching Failed to allocate 84 bytes
test_log_state_routes_capsules_to_log_capsules test_viewer_geometry_batching Failed to allocate 56 bytes
test_picking_setup_device_cuda_0 test_viewer_picking Failed to allocate 28 bytes
test_picking_setup_device_cuda_1 test_viewer_picking Failed to allocate 28 bytes

The viewer geometry batching and viewer picking tests have nothing to do with SDF textures — they failed only because they were dispatched to a worker whose CUDA context was poisoned.

With process isolation: 3 errors
Only the actual test_sdf_texture failures remain — the 4 cascaded failures in unrelated modules are eliminated:

Test Module Error
test_uint16_vs_nanovdb_distance_cuda_1 test_sdf_texture CUDA error 700 (root cause)
test_uint16_vs_nanovdb_gradient_cuda_0 test_sdf_texture Failed to allocate 12 bytes
test_uint16_vs_nanovdb_gradient_cuda_1 test_sdf_texture Failed to allocate 12 bytes

The remaining 3 errors are all within the same test_sdf_texture suite (the deliberately reintroduced bug from reverting #2226).

Summary by CodeRabbit

  • Tests
    • Test runner now disables per-process task reuse on multi‑GPU systems to avoid cascading CUDA errors; a CLI option allows toggling this while single‑GPU throughput is unchanged.
  • Documentation
    • Clarified CLI help to explain which backend the process‑pool option affects and when recycling is auto‑enabled.
    • Added changelog entry describing the multi‑GPU process‑reuse change.

@coderabbitai

coderabbitai Bot commented Mar 25, 2026 •

Copy link
Copy Markdown
Contributor

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

Clarified CLI help for --disable-process-pooling to indicate it maps to max_tasks_per_child=1 for concurrent.futures.ProcessPoolExecutor. Refactored executor creation to build executor_kwargs and, on Python ≥3.11, set max_tasks_per_child=1 when the flag is used or multiple CUDA devices are detected.

Changes

Cohort / File(s) Summary
Parallel test runner
newton/tests/thirdparty/unittest_parallel.py
Updated --disable-process-pooling help text; moved executor parameters into an executor_kwargs dict and, for Python ≥3.11, conditionally add max_tasks_per_child=1 when the flag is set or wp.get_cuda_device_count() > 1, then create the executor with ProcessPoolExecutor(**executor_kwargs). The multiprocessing.Pool branch is unchanged.
Changelog
CHANGELOG.md
Added an Unreleased "Changed" note stating the test runner disables process reuse on multi-GPU systems (to avoid cascading CUDA errors) while retaining reuse on single-GPU systems for throughput.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • eric-heiden
  • Kenny-Vilella
🚥 Pre-merge checks | ✅ 5
✅ 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 directly and clearly summarizes the main change: preventing CUDA error cascade on multi-GPU test runs by implementing process isolation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@shi-eric shi-eric self-assigned this Mar 25, 2026
@shi-eric shi-eric added testing Issues that are related to unit and other types of testing automation Issues related to ci/cd and automation in general labels Mar 25, 2026
@codecov

codecov Bot commented Mar 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@newton/tests/thirdparty/unittest_parallel.py`:
- Line 236: The change made max_tasks_per_child=1 unconditionally (breaking the
--disable-process-pooling CLI flag); update the ProcessPoolExecutor construction
so that max_tasks_per_child=1 is only set when process pooling is enabled by
checking args.disable_process_pooling (e.g., pass max_tasks_per_child=1
conditionally or omit the kw arg when args.disable_process_pooling is True), use
the args.disable_process_pooling symbol and the max_tasks_per_child kwarg near
the ProcessPoolExecutor/bootstrap code in unittest_parallel.py to locate the
spot, and ensure the CLI behavior is preserved (or if you intentionally remove
the option instead, remove the flag and add a [Unreleased]/Fixed entry in
CHANGELOG.md).
- Around line 234-237: The ProcessPoolExecutor call using max_tasks_per_child is
only valid on Python 3.11+, so change the block that constructs the worker pool
(the with concurrent.futures.ProcessPoolExecutor(...
mp_context=multiprocessing.get_context(...), max_workers=process_count,
max_tasks_per_child=1) call) to first check sys.version_info >= (3,11) (or
attempt creation and fall back on TypeError), and otherwise use the existing
multiprocessing.Pool(..., processes=process_count, maxtasksperchild=1,
context=...) fallback; preserve honoring the existing --disable-process-pooling
flag so that when pooling is disabled you still use the non-recycling path, and
ensure mp_context and process_count variables are passed appropriately in both
branches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a9cfd092-1902-42db-9e8f-031e46f86bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 76b1276 and 7529d7b.

📒 Files selected for processing (1)
  • newton/tests/thirdparty/unittest_parallel.py

Comment thread newton/tests/thirdparty/unittest_parallel.py Outdated
Comment thread newton/tests/thirdparty/unittest_parallel.py Outdated
@shi-eric
shi-eric force-pushed the ershi/fix-unittest-error-cascade branch 2 times, most recently from 9533b79 to ac52e41 Compare March 25, 2026 20:39

@adenzler-nvidia adenzler-nvidia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good — correct version guard for max_tasks_per_child (Python 3.11+), consistent with the existing maxtasksperchild behavior in the multiprocessing.Pool path, and well-documented help text update.

@adenzler-nvidia
adenzler-nvidia added this pull request to the merge queue Mar 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Mar 30, 2026
@dsmither-lgtm dsmither-lgtm added this to the 1.1 Release milestone Mar 30, 2026
@shi-eric
shi-eric marked this pull request as draft March 30, 2026 17:16
@shi-eric

Copy link
Copy Markdown
Member Author

Setting this back to draft mode. The time it takes to run the entire suite slowed down more than expected, especially on Windows.

@shi-eric
shi-eric force-pushed the ershi/fix-unittest-error-cascade branch from ac52e41 to b66e87a Compare April 1, 2026 16:33
@shi-eric shi-eric changed the title Set max_tasks_per_child=1 to prevent CUDA error cascade Prevent CUDA error cascade on multi-GPU test runs Apr 1, 2026
@shi-eric
shi-eric force-pushed the ershi/fix-unittest-error-cascade branch from b66e87a to a6df162 Compare April 4, 2026 22:09
@shi-eric
shi-eric marked this pull request as ready for review April 4, 2026 22:22
@shi-eric

shi-eric commented Apr 4, 2026

Copy link
Copy Markdown
Member Author

@adenzler-nvidia this one should be ready again. Due to the additional overheads, I only enable this setting when the system has multiple GPUs.

@preist-nvidia preist-nvidia modified the milestones: 1.1 Release, 1.2 Release Apr 7, 2026
@preist-nvidia

Copy link
Copy Markdown
Member

Pushing to 1.2 not critical to get into 1.1.

@adenzler-nvidia

Copy link
Copy Markdown
Member

oh looks like I missed that ping. This PR is still relevant, right?

@shi-eric

Copy link
Copy Markdown
Member Author

oh looks like I missed that ping. This PR is still relevant, right?

yes

@shi-eric
shi-eric force-pushed the ershi/fix-unittest-error-cascade branch from a6df162 to 9c6f3f2 Compare April 24, 2026 01:49

@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 the current code and only fix it if needed.

Inline comments:
In `@newton/tests/thirdparty/unittest_parallel.py`:
- Around line 242-243: The current check only applies
args.disable_process_pooling when sys.version_info >= (3, 11), making the flag
silently no-op on Python 3.10; add a one-line stderr warning when
args.disable_process_pooling is True but sys.version_info < (3, 11) so users
know the flag is ineffective. Detect this right where sys.version_info and
args.disable_process_pooling are evaluated (the same block that sets
executor_kwargs["max_tasks_per_child"]) and write a short warning to stderr (or
use the existing logger) explaining that --disable-process-pooling requires
Python 3.11+. Ensure the message runs only when the flag is set and Python
version < (3, 11).
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: d7fbdffb-5a92-4bf2-9655-45fb82f2e2bc

📥 Commits

Reviewing files that changed from the base of the PR and between a6df162 and 9c6f3f2.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • newton/tests/thirdparty/unittest_parallel.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

Comment thread newton/tests/thirdparty/unittest_parallel.py

@adenzler-nvidia adenzler-nvidia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM — could you just move the CHANGELOG entry to the [Unreleased] section? After merging main it lands in the released [1.1.0] block.

Replace worker processes after each test suite to prevent a fatal CUDA
error in one test from corrupting the context for subsequent tests in
the same worker process.
The max_tasks_per_child parameter for ProcessPoolExecutor was added in
Python 3.11. Build the executor kwargs conditionally so the test runner
remains compatible with Python 3.10 while still recycling workers on
3.11+ to prevent CUDA error cascade.
Explain that the flag only affects the multiprocessing.Pool backend and
that the concurrent.futures backend always recycles workers via
max_tasks_per_child=1 on Python 3.11+.
Only set max_tasks_per_child=1 when multiple CUDA devices are detected
or --disable-process-pooling is explicitly passed. This restores process
reuse on single-GPU systems for better test throughput while still
preventing CUDA error cascade on multi-GPU setups.
@adenzler-nvidia
adenzler-nvidia added this pull request to the merge queue Apr 24, 2026
Merged via the queue into newton-physics:main with commit cbd96be Apr 24, 2026
25 checks passed
@shi-eric
shi-eric deleted the ershi/fix-unittest-error-cascade branch May 8, 2026 19:21
@coderabbitai coderabbitai Bot mentioned this pull request May 13, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation Issues related to ci/cd and automation in general testing Issues that are related to unit and other types of testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants