Skip to content

rccl: add multithread support to NetIbMPI validation suite - #9973

Merged
i-kosarev merged 7 commits into
developfrom
netibmpi-multithread-support
Aug 19, 2026
Merged

i-kosarev merged 7 commits into
developfrom
netibmpi-multithread-support

Conversation

@i-kosarev

@i-kosarev i-kosarev commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The NetIbMPI validation suite (rccl-UnitTestsMPI) previously exercised the
NET/IB transport through one host worker per MPI rank. This change adds
coverage for the safe multi-worker model used by rccl-tests -t: independent
transport contexts and connections driven concurrently within one process.

Two things are intentionally out of scope. Concurrent calls on one NET/IB
communicator: NCCL communicators are reentrant but not thread-safe, and the
NET/IB send/receive communicator contains shared FIFO, request, and
work-request state, so such a test would exercise an unsupported contract
rather than validate rccl-tests behavior. Concurrent plugin
init/finalize: the plugin keeps process-global device tables and a
reference count, so worker contexts are created and destroyed by the main
thread. This change is therefore confined to the test suite and needs no
transport code changes.

Technical Details

  • Adds --net_ib_nthreads=N to rccl-UnitTestsMPI. The default is 1;
    positive values above 16 clamp to 16; malformed or conflicting values
    fail before GTest starts. Conflicts are detected on the values as written,
    before clamping, so two distinct out-of-range requests are rejected rather
    than silently collapsing to the same capped value. All MPI ranks verify that
    they resolved the same effective value.
  • Preserves the original single-threaded paths for N=1.
  • Adds a main-thread control plane for the threaded path: the main thread
    creates every worker context and connection, synchronizes setup failures
    across ranks before data transfer, and begins teardown only after all
    workers complete.
  • Adds a two-stage worker start gate. Worker bodies do not call MPI or GTest;
    they return ThreadResult, which the main thread reports after joining.
  • Covers the independent worker model through SimpleSendRecv and
    MultipleSequentialTransfers, with runner configurations at 2, 4, and 16
    workers per rank. Every other NetIB test ignores the option and keeps its
    single-worker behavior.
  • Asserts that all N workers are concurrently inside the body, not merely
    that two overlap. The start/body gate makes that peak deterministic, so the
    exact bound also catches a regression in the gate itself; a scheduler that
    serialized the workers fails the test rather than reporting false coverage.
  • Checks RDMA resource counts around the threaded paths to catch QP, CQ, MR,
    or PD leaks. Counts are scoped to the test process by PID with no
    system-wide fallback, so an unrelated process on a shared node cannot
    perturb the comparison and both snapshots are always taken the same way.
  • Guards the host-VMM registration tests on HIP SDKs that do not declare
    hipMemLocationTypeHost, so unsupported runtimes skip only that scenario.
  • Documents the option, the threading model it reproduces, and the concurrency
    deliberately left untested as a multithread-validation module of the
    feature-unit-testing skill.

No public RCCL API or ABI changes are introduced.

Issue Tracking

JIRA ID: AICOMNET-323

Test Plan

Hardware: mia1, AINIC/Pensando with RoCE on rdma0..rdma3, gfx950, host
build.

  • Rebuild rccl-UnitTestsMPI in a debug configuration with MPI tests enabled.
  • Run the independent worker matrix at
    --net_ib_nthreads=2, 4, and 16.
  • Verify CLI behavior:
    • --net_ib_nthreads=99 clamps to 16 and passes;
    • --net_ib_nthreads=abc is rejected;
    • conflicting duplicate values are rejected.
  • Run the complete NetIB transport configuration through isolated mpirun
    processes.

The functional acceptance target for this change is Basic coverage of the
independent-context lifecycle and data paths. Branch-coverage measurement of
the new failure-control paths remains follow-up work.

Test Result

On mia1-p01-g25 ↔ mia1-p01-g27, one rank per node with eight GPUs available
per node:

  • Threaded matrix at 2, 4, and 16 workers per rank:
    6 PASS / 0 FAIL / 0 HANG / 0 SKIP / 0 ERROR.
  • Full NetIB configuration: 339 PASS / 0 FAIL / 0 HANG / 6 SKIP / 2 ERROR
    (347 total).
  • A single-node repeat with both ranks on mia1-p01-g30 gives the same totals
    and the same non-passing tests.
  • The two errors are the established baseline failures:
    NetIB_CtsDepthStress (MPI_Cancel abort, rc=7) and
    Cast_MaxQPCount128 (SIGSEGV, rc=139).
  • The six skips are capability/configuration preconditions:
    RapidConnectDisconnect for NET/IB and IB-CAST plus four WRR scheduler
    tests that require a longer scheduler update interval.
  • CLI behavior confirmed: --net_ib_nthreads=99 clamps to 16 and passes,
    --net_ib_nthreads=abc is rejected before GTest, and conflicting duplicate
    values are rejected.

Re-verified after the review-fix commit on mia1-p01-g24 ↔ mia1-p01-g25,
one rank per node. This run covers the changed paths, not the full suite; the
totals above still refer to the complete configuration run on the parent
commit.

  • Threaded matrix at 2, 4, and 16 workers per rank with the exact-peak
    fan-out assertion and the PID-scoped RDMA counting:
    6 PASS / 0 FAIL / 0 HANG / 0 SKIP / 0 ERROR.
  • CLI parser matrix, five cases: 99 100 rejected as conflicting, 99 99
    and 99 clamp to 16 and pass, 2 4 rejected as conflicting, abc
    rejected as malformed.
  • Negative test for the clamp-ordering fix: rebuilt with the pre-fix parser,
    --net_ib_nthreads=99 --net_ib_nthreads=100 is accepted and the test
    passes; with the fix it is rejected. The reported defect reproduces.
  • rdma resource show on this cluster emits no pid field for kernel-owned
    objects, so the previous filter matched nothing and every snapshot used the
    system-wide fallback path. Confirmed directly on the node, which is what
    motivated removing the fallback rather than adjusting the match.

Submission Checklist

@i-kosarev
i-kosarev requested review from a team and a lite review from Copilot August 11, 2026 10:32
@therock-pr-bot

therock-pr-bot Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ⚠️ Warning Error: Source/code files changed without an accompanying unit test.
Expected: add at least one test file named like test_<name>.py / test_<name>.cpp (or <name>_test.*).
Current: code file(s) changed: projects/rccl/test/common/MPIEnvironment.hpp, projects/rccl/test/common/main_mpi.cpp, projects/rccl/test/transport/NetIbMPI/GeneralTests.cpp, projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp; no test file found
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

Copy link
Copy Markdown

🚫 Please fix the failed policies before requesting reviews.

The following policy checks failed:

  • ❌ PR Description

The Not ready to Review label has been added to this PR.
Once all policies pass, the label will be removed automatically.

Copilot AI 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.

Pull request overview

Adds CLI-controlled multithreading support to the NetIbMPI transport validation tests, enabling two stress modes (independent per-thread connections and shared-connection concurrent ops) and wiring new test-runner suites to exercise them; also includes a HIP feature-detection build fix for older runtimes.

Changes:

  • Add --net_ib_nthreads=N parsing (stripped before GTest) and store it on MPIEnvironment::nThreads.
  • Introduce multithread execution helpers in NetIbMPITestBase.hpp and update existing NetIbMPI tests to run single-threaded by default, multithreaded when nThreads>1.
  • Add test-runner suites for the new multithread coverage and add a CMake/HIP compile-time feature guard for hipMemLocationTypeHost.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
projects/rccl/tools/scripts/test_runner/configs/net_ib_transport.json Adds new multithread NetIb transport suites using --net_ib_nthreads=4.
projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp Adds thread fan-out helpers and per-thread init/connect/teardown utilities.
projects/rccl/test/transport/NetIbMPI/GeneralTests.cpp Parameterizes key NetIb send/recv tests to run in multithread modes.
projects/rccl/test/transport/NetIbMPI/CastTests.cpp Extends a CAST large-transfer test to run concurrently on a shared connection.
projects/rccl/test/RegistrationMPITests.cpp Adds compile-time guard handling when hipMemLocationTypeHost is unavailable.
projects/rccl/test/common/MPIEnvironment.hpp Adds global MPIEnvironment::nThreads setting (default 1).
projects/rccl/test/common/main_mpi.cpp Parses/strips --net_ib_nthreads= before MPI/GTest initialization.
projects/rccl/test/CMakeLists.txt Defines RCCL_HAS_HIP_MEM_LOCATION_TYPE_HOST when feature detection succeeds.
projects/rccl/CMakeLists.txt Adds check_cxx_source_compiles probe for hipMemLocationTypeHost.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@i-kosarev
i-kosarev marked this pull request as draft August 11, 2026 11:25
i-kosarev added a commit that referenced this pull request Aug 11, 2026
…lpers

Fixes two bugs flagged by GitHub Copilot's automated review on PR #9973:

1. SetupConnectionForThread: several error paths returned before reaching
   MPI_Send/MPI_Recv/MPI_Barrier. If one side of a connection attempt failed
   early while its peer had already issued the matching blocking MPI call
   (e.g. rank 1's MPI_Recv, or either side's MPI_Barrier), the peer would
   hang forever waiting for a message/barrier call that would never come.
   Fixed by falling through to the unconditional MPI_Send / MPI_Barrier on
   every path (break instead of return), so call counts always stay
   symmetric across ranks and threads regardless of which side failed.

2. RunMultiThreadedIndependent: the failure branch after a failed
   SetupConnectionForThread called only net_->finalize(ctx) instead of
   TeardownConnectionForThread(ctx, pair, rank). Per net_ib.cc,
   ncclIbFinalize() only does free(ctx) + ncclIbFinalizeDevices() — it never
   closes comm handles. If setup had already created listenComm/recvComm/
   sendComm before failing, that handle leaked. Fixed by using the full
   teardown helper, which is a no-op for any handle that was never created.

Verified: rebuilt rccl-UnitTestsMPI (AINIC/mia1) and re-ran all 4
multithread suites end to end — 4/4 PASS (SimpleSendRecv,
MultipleSequentialTransfers, SendRecvMultipleSizes, CastLargeTransfer).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

RCCL Perf-Regression Gate: ⚠️ NO VERDICT (not measured)

This run did not produce a usable answer. It is neither a PASS nor a regression — treat the perf gate as not run for this change.

Why:

  • this report predates the trustworthiness flag, or a group could not be scored

Mode: detect (reference vs candidate)
Thresholds: small 17.2% · mid 12.3% · large 12.9%
Keys compared: 0 · Confirmed regressions: 0 · Inconclusive: 0
Provenance: n/a

Per-collective breakdown
group keys regressions inconclusive
all_gather_perf-d=bfloat16-default 0 0 0
all_gather_perf-d=float-default 0 0 0
all_reduce_perf-d=bfloat16-default 0 0 0
all_reduce_perf-d=float-default 0 0 0
broadcast_perf-d=bfloat16-default 0 0 0
broadcast_perf-d=float-default 0 0 0
reduce_scatter_perf-d=bfloat16-default 0 0 0
reduce_scatter_perf-d=float-default 0 0 0

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (3)

projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp:704

  • SkipUnlessMpiThreadMultiple() says to “Skip instead”, but it records an ADD_FAILURE() and returns, which turns unsupported MPI builds into hard test failures rather than clean skips. That also contradicts the PR description’s intent to avoid undefined behavior/hangs when MPI_THREAD_MULTIPLE isn’t granted.
    // Both RunMultiThreaded* helpers issue MPI calls (barriers, handle
    // exchange) from worker threads, which is only legal at
    // MPI_THREAD_MULTIPLE. MPIHelpers::initializeMPI() requests that level but
    // an MPI build may grant less, in which case those calls are undefined
    // behavior and typically manifest as an unexplained hang. Skip instead.
    bool SkipUnlessMpiThreadMultiple() {
        if (MPIEnvironment::mpiThreadSupport >= MPI_THREAD_MULTIPLE) return false;
        ADD_FAILURE() << "multithread mode requires MPI_THREAD_MULTIPLE ("
                      << MPI_THREAD_MULTIPLE << "), but MPI provided "
                      << MPIEnvironment::mpiThreadSupport

projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp:755

  • On rank 0, if CreateListenCommCtx() fails, pair.handle may contain uninitialized bytes but is still sent via MPI_Send. This can trigger UB/sanitizer findings and makes failures harder to debug; initialize the handle before the possible failure path.
        ThreadResult result;
        if (rank == 0) {
            if (CreateListenCommCtx(ctx, dev, &pair.handle, &pair.listenComm) != ncclSuccess) {
                result.ok = false;
                result.msg = "CreateListenComm failed";

projects/rccl/test/transport/NetIbMPI/GeneralTests.cpp:405

  • In the multithreaded (nThreads>1) path, the loop reuses the same tag for every message size (threadIdx*kThreadTagStride+100). Since WaitForCompletion() only waits on the local request, one rank can potentially advance and post a new op with the same tag while the peer is still completing the prior one, risking tag/request collisions. Make the tag unique per iteration (e.g., include size or the loop index).
        ThreadResult result;
        for (size_t size : testSizes) {
            const int tag = threadIdx * kThreadTagStride + 100;
            const int seed = threadIdx * 100000 + 2000 + static_cast<int>(size);

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (2)

projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp:855

  • tag passed as mpiTag to MPI_Send/MPI_Recv scales with kThreadTagStride (10000). With --net_ib_nthreads clamped to 16, this can produce MPI tags up to 150000, which may exceed MPI_TAG_UB on some MPI implementations (the standard minimum is 32767). Use a separate small MPI tag namespace for the handle exchange (e.g., 1000 + threadIdx) so multithread mode stays portable.
            const int tag = threadIdx * kThreadTagStride;
            ThreadResult setupResult = SetupConnectionForThread(ctx, dev, pair, rank, peerRank, tag);

projects/rccl/test/common/MPIEnvironment.hpp:108

  • The docstring says callers should “skip” when mpiThreadSupport is below MPI_THREAD_MULTIPLE, but the new multithread helpers deliberately ADD_FAILURE() and return (not skip). Align the documentation with the implemented behavior to avoid confusion for future test authors.
     * MPI_THREAD_MULTIPLE is requested by MPIHelpers::initializeMPI(), but an
     * MPI build may grant less. Test paths that issue MPI calls from worker
     * threads must check this and skip when it is below MPI_THREAD_MULTIPLE,
     * otherwise those calls are undefined behavior (typically a silent hang).

Feature-detect hipMemLocationTypeHost so MPI registration tests build on SDKs
that lack the enum and skip only host-VMM scenarios they cannot represent.

Co-authored-by: Cursor <cursoragent@cursor.com>
@i-kosarev
i-kosarev marked this pull request as ready for review August 13, 2026 09:29
@i-kosarev
i-kosarev force-pushed the netibmpi-multithread-support branch 3 times, most recently from 92240d4 to 76062dc Compare August 13, 2026 18:26
i-kosarev and others added 4 commits August 13, 2026 19:30
Add strict rank-consistent worker configuration, main-thread MPI coordination,
and gated independent NetIB workers so rccl-tests-style threaded data paths are
exercised without sharing an unsupported communicator handle.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use the fixture-level resource snapshot helper in threaded tests so the new
leak assertions compile and share the established NetIB counting behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Hold every worker at a second gate after it has entered the threaded phase so
fan-out validation does not depend on host scheduler timing.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add a multithread-validation module covering which production threading a
plugin-level test should reproduce, which concurrency is not worth testing,
worker-option parsing, and making worker overlap an assertion.

Co-authored-by: Cursor <cursoragent@cursor.com>
@i-kosarev
i-kosarev force-pushed the netibmpi-multithread-support branch from 76062dc to 5f5b739 Compare August 13, 2026 19:31
@i-kosarev
i-kosarev requested a lite review from Copilot August 13, 2026 21:02

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (1)

projects/rccl/test/transport/NetIbMPI/GeneralTests.cpp:566

  • The leading comment block for MultipleSequentialTransfers still describes a "two barriers per iteration" synchronization strategy, but the new threaded path intentionally removes per-iteration MPI barriers. The newly added note should explicitly scope the two-barrier description to the nThreads==1 path, otherwise the test documentation is misleading for the new behavior.
// Parameterized by MPIEnvironment::nThreads. Every worker has an independent
// connection and buffer. N=1 retains the two per-transfer MPI barriers; the
// threaded path leaves rank synchronization to the main-thread harness after
// every worker has completed.

@nikitaxgusev nikitaxgusev 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. Test-only change, no public API/ABI impact. The threading model is sound: MPI stays on the main thread, worker bodies use return-code wrappers only (no fatal GTest macros), rank control flow stays symmetric, and the two-gate design makes overlap deterministic. A few non-blocking notes below.

Comment thread projects/rccl/test/common/main_mpi.cpp Resolved
@i-kosarev
i-kosarev requested review from a team, atulkulk and sdarisala August 17, 2026 09:54
Four review comments on the NetIbMPI multithread harness, all in test code.

Reject conflicting --net_ib_nthreads values before clamping. The conflict
check ran after the clamp, so two distinct out-of-range requests (99 and
100) both collapsed to kMaxThreads and were accepted as non-conflicting.
Verified on hardware: the pre-fix binary accepts `--net_ib_nthreads=99
--net_ib_nthreads=100` and runs the test; the fixed binary rejects it.

Count only this process's RDMA objects, with no system-wide fallback.
`rdma resource show` omits the pid field entirely for kernel-owned objects
(they carry "comm [ib_core]"), so on this cluster the pid filter matched
nothing and every snapshot silently fell back to a system-wide count. A
process that acquired or released its last object between the two
snapshots therefore compared counts taken in two different modes, turning
a real leak into a negative delta and an unrelated neighbour process into
a spurious leak failure. Zero matches now correctly means zero owned
objects. Drops CountNonEmptyLines, whose only caller was that fallback.

Assert the exact worker peak instead of ">= 2". bodyGate holds every
worker at the top of the body until all N arrive, so the peak is
deterministically N; the loose bound would still pass if the gate stopped
blocking and two workers merely overlapped by chance.

Document why the worker receive path treats a NULL request as fatal while
the send path retries: each worker owns its recvComm and posts exactly one
receive, so the FIFO cannot be full and NULL is a contract violation
rather than backpressure.

Verified on mia1, 2 nodes (g24+g25), gfx950, AINIC, host build:
SimpleSendRecv and MultipleSequentialTransfers pass at
--net_ib_nthreads=2/4/16, and the five CLI parser cases behave as
specified.

Copilot AI 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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (1)

projects/rccl/test/transport/NetIbMPI/NetIbMPITestBase.hpp:788

  • maxInFlight is incremented before bodyGate and before body() is invoked. Once all workers reach this gate, the recorded maximum is therefore guaranteed to be nThreads, even if the scheduler executes every transport body serially after the gate opens. This makes the new fan-out assertion unable to verify the PR's intended concurrent NET/IB data-path coverage. Move the synchronization/measurement point into the bodies after each worker has posted an outstanding transport request (for example, via a callback/gate passed to the body) so no request can complete before all workers have entered the actual data path.
            const int active = inFlight.fetch_add(1, std::memory_order_relaxed) + 1;
            UpdateMaximum(maxInFlight, active);
            // Do not let an eagerly scheduled worker complete its body before
            // the other workers have actually entered it. This makes the
            // concurrency assertion deterministic rather than scheduler-timing
            // dependent.
            if (!bodyGate.ArriveAndWait()) {

@i-kosarev i-kosarev mentioned this pull request Aug 18, 2026
1 task done
develop grew its own guard for the host-VMM tests, keyed on ROCM_VERSION rather
than on a CMake probe for hipMemLocationTypeHost. Kept develop's version and
dropped the probe, which nothing else used.

Co-authored-by: Cursor <cursoragent@cursor.com>

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

Out of curiosity, I see that for both the SimpleSendRecv and MultipleSequentialTransfers tests, we call RunMultiThreadedIndependent() for device 0, even though the nodes have four RDMA devices each. Is this intentional? Is there a plan to also support multiple worker threads spanning multiple devices in the future?

@i-kosarev

Copy link
Copy Markdown
Contributor Author

Out of curiosity, I see that for both the SimpleSendRecv and MultipleSequentialTransfers tests, we call RunMultiThreadedIndependent() for device 0, even though the nodes have four RDMA devices each. Is this intentional? Is there a plan to also support multiple worker threads spanning multiple devices in the future?

Yes, that's intentional: it is worth racing here per device, pointing every worker at device 0 is what creates the contention. And also I am introducing workers spreading in follow-up PR #10301 via ThreadDevPolicy::Spread()

@i-kosarev
i-kosarev merged commit 5d05d04 into develop Aug 19, 2026
32 of 36 checks passed
@i-kosarev
i-kosarev deleted the netibmpi-multithread-support branch August 19, 2026 11:25
i-kosarev added a commit that referenced this pull request Aug 19, 2026
#9973 landed on develop as a squash, so the shared harness, config and skill-doc
content arrives from an unrelated line of history and conflicts with the copies
this branch already carries. For all three files develop's content is identical
to the base this branch was validated against, so the resolution keeps this
branch's versions, which are that same content plus the expansion. The two
cluster configs that gained GIN suites upstream merged without a conflict, and
the resulting tree differs from develop only by the expansion itself.

Co-authored-by: Cursor <cursoragent@cursor.com>
cmcknigh pushed a commit that referenced this pull request Aug 25, 2026
Motivation
The NetIbMPI validation suite (rccl-UnitTestsMPI) previously exercised the
NET/IB transport through one host worker per MPI rank. This change adds
coverage for the safe multi-worker model used by rccl-tests -t: independent
transport contexts and connections driven concurrently within one process.

Two things are intentionally out of scope. Concurrent calls on one NET/IB
communicator: NCCL communicators are reentrant but not thread-safe, and the
NET/IB send/receive communicator contains shared FIFO, request, and
work-request state, so such a test would exercise an unsupported contract
rather than validate rccl-tests behavior. Concurrent plugin
init/finalize: the plugin keeps process-global device tables and a
reference count, so worker contexts are created and destroyed by the main
thread. This change is therefore confined to the test suite and needs no
transport code changes.

Technical Details
Adds --net_ib_nthreads=N to rccl-UnitTestsMPI. The default is 1;
positive values above 16 clamp to 16; malformed or conflicting values
fail before GTest starts. Conflicts are detected on the values as written,
before clamping, so two distinct out-of-range requests are rejected rather
than silently collapsing to the same capped value. All MPI ranks verify that
they resolved the same effective value.
Preserves the original single-threaded paths for N=1.
Adds a main-thread control plane for the threaded path: the main thread
creates every worker context and connection, synchronizes setup failures
across ranks before data transfer, and begins teardown only after all
workers complete.
Adds a two-stage worker start gate. Worker bodies do not call MPI or GTest;
they return ThreadResult, which the main thread reports after joining.
Covers the independent worker model through SimpleSendRecv and
MultipleSequentialTransfers, with runner configurations at 2, 4, and 16
workers per rank. Every other NetIB test ignores the option and keeps its
single-worker behavior.
Asserts that all N workers are concurrently inside the body, not merely
that two overlap. The start/body gate makes that peak deterministic, so the
exact bound also catches a regression in the gate itself; a scheduler that
serialized the workers fails the test rather than reporting false coverage.
Checks RDMA resource counts around the threaded paths to catch QP, CQ, MR,
or PD leaks. Counts are scoped to the test process by PID with no
system-wide fallback, so an unrelated process on a shared node cannot
perturb the comparison and both snapshots are always taken the same way.
Guards the host-VMM registration tests on HIP SDKs that do not declare
hipMemLocationTypeHost, so unsupported runtimes skip only that scenario.
Documents the option, the threading model it reproduces, and the concurrency
deliberately left untested as a multithread-validation module of the
feature-unit-testing skill.
No public RCCL API or ABI changes are introduced.
i-kosarev added a commit that referenced this pull request Sep 2, 2026
Motivation
#9973 added --net_ib_nthreads and exercised it on two host-memory tests. Extending that to the rest of the NET/IB matrix needs data-path helpers a worker can call, which is what this adds, with two guards against a global lock or a fault-injection shim reaching the progress path. Three runner defects turned up while wiring the suites in, each quietly dropping coverage, and they are fixed here so the rest of the stack lands on something that works.

First of four stacked PRs: independent connections, IB-CAST with fused devices, and fault injection follow.

Technical Details
The ordinary transfer helpers barrier and assert fatally, so a worker cannot use
them: a fatal assertion only returns from the worker, and a worker-side collective
strands the peer rank when a sibling fails. The worker-safe versions return a
ThreadResult the main thread reports after joining. Workers spread across NICs
with a routable GID instead of all landing on device 0, and the rank's HIP device
is propagated into them, since the current device is thread-local.

The timing gate polls without sleeping, unlike the functional tests: the harness
backs off 10 ms both while waiting for a completion and while waiting for the
receiver's FIFO slot, which is longer than everything being measured, so with the
backoff a phase reports the poll interval rather than the plugin.

Threaded suites extend a base that declares no tests, because extends merges test
lists rather than replacing them: extending a suite that carries tests would
silently run those too, at the wrong worker count.
corey-derochie-amd pushed a commit that referenced this pull request Oct 14, 2026
Motivation
#9973 added --net_ib_nthreads and exercised it on two host-memory tests. Extending that to the rest of the NET/IB matrix needs data-path helpers a worker can call, which is what this adds, with two guards against a global lock or a fault-injection shim reaching the progress path. Three runner defects turned up while wiring the suites in, each quietly dropping coverage, and they are fixed here so the rest of the stack lands on something that works.

First of four stacked PRs: independent connections, IB-CAST with fused devices, and fault injection follow.

Technical Details
The ordinary transfer helpers barrier and assert fatally, so a worker cannot use
them: a fatal assertion only returns from the worker, and a worker-side collective
strands the peer rank when a sibling fails. The worker-safe versions return a
ThreadResult the main thread reports after joining. Workers spread across NICs
with a routable GID instead of all landing on device 0, and the rank's HIP device
is propagated into them, since the current device is thread-local.

The timing gate polls without sleeping, unlike the functional tests: the harness
backs off 10 ms both while waiting for a completion and while waiting for the
receiver's FIFO slot, which is longer than everything being measured, so with the
backoff a phase reports the poll interval rather than the plugin.

Threaded suites extend a base that declares no tests, because extends merges test
lists rather than replacing them: extending a suite that carries tests would
silently run those too, at the wrong worker count.
corey-derochie-amd pushed a commit that referenced this pull request Oct 15, 2026
Motivation
#9973 added --net_ib_nthreads and exercised it on two host-memory tests. Extending that to the rest of the NET/IB matrix needs data-path helpers a worker can call, which is what this adds, with two guards against a global lock or a fault-injection shim reaching the progress path. Three runner defects turned up while wiring the suites in, each quietly dropping coverage, and they are fixed here so the rest of the stack lands on something that works.

First of four stacked PRs: independent connections, IB-CAST with fused devices, and fault injection follow.

Technical Details
The ordinary transfer helpers barrier and assert fatally, so a worker cannot use
them: a fatal assertion only returns from the worker, and a worker-side collective
strands the peer rank when a sibling fails. The worker-safe versions return a
ThreadResult the main thread reports after joining. Workers spread across NICs
with a routable GID instead of all landing on device 0, and the rank's HIP device
is propagated into them, since the current device is thread-local.

The timing gate polls without sleeping, unlike the functional tests: the harness
backs off 10 ms both while waiting for a completion and while waiting for the
receiver's FIFO slot, which is longer than everything being measured, so with the
backoff a phase reports the poll interval rather than the plugin.

Threaded suites extend a base that declares no tests, because extends merges test
lists rather than replacing them: extending a suite that carries tests would
silently run those too, at the wrong worker count.
corey-derochie-amd pushed a commit that referenced this pull request Oct 15, 2026
Motivation
#9973 added --net_ib_nthreads and exercised it on two host-memory tests. Extending that to the rest of the NET/IB matrix needs data-path helpers a worker can call, which is what this adds, with two guards against a global lock or a fault-injection shim reaching the progress path. Three runner defects turned up while wiring the suites in, each quietly dropping coverage, and they are fixed here so the rest of the stack lands on something that works.

First of four stacked PRs: independent connections, IB-CAST with fused devices, and fault injection follow.

Technical Details
The ordinary transfer helpers barrier and assert fatally, so a worker cannot use
them: a fatal assertion only returns from the worker, and a worker-side collective
strands the peer rank when a sibling fails. The worker-safe versions return a
ThreadResult the main thread reports after joining. Workers spread across NICs
with a routable GID instead of all landing on device 0, and the rank's HIP device
is propagated into them, since the current device is thread-local.

The timing gate polls without sleeping, unlike the functional tests: the harness
backs off 10 ms both while waiting for a completion and while waiting for the
receiver's FIFO slot, which is longer than everything being measured, so with the
backoff a phase reports the poll interval rather than the plugin.

Threaded suites extend a base that declares no tests, because extends merges test
lists rather than replacing them: extending a suite that carries tests would
silently run those too, at the wrong worker count.
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.

4 participants