Skip to content

Add: support mpirun-launched multi-host L3 workers - #1623

Open
sunkaixuan2018 wants to merge 1 commit into
hw-native-sys:mainfrom
sunkaixuan2018:add-mpi-run
Open

Add: support mpirun-launched multi-host L3 workers#1623
sunkaixuan2018 wants to merge 1 commit into
hw-native-sys:mainfrom
sunkaixuan2018:add-mpi-run

Conversation

@sunkaixuan2018

@sunkaixuan2018 sunkaixuan2018 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Dependency

Summary

  • add static MPI-launched L3 worker groups with rank-0 manifest broadcast, ready coordination, and deterministic worker topology
  • extend Global CommDomains from the Add: support mixed local and remote L4 CommDomains #1456 local/TCP foundation to MPI-backed L3 nodes
  • add regression coverage for control-ID uniqueness and imported window/buffer extents

Testing

  • pytest tests/ut/py/test_global_comm_domain.py -m "not requires_hardware" on the validated implementation (25 passed)
  • three consecutive two-host A3 2x2 runs (all exit 0; four-rank compute/communication max_diff == 0)
  • verified manifest broadcast, ready handshake, PREPARE_EXPORT, MPI allgather, IMPORT, COMMIT, copy, release, and process/shared-memory cleanup
  • current aligned commit: headers, English-only, YAML, whitespace, Ruff, Pyright, and Python syntax checks passed
  • current aligned commit full pytest was not rerun on the Windows preparation host because the project setup imports the Unix-only fcntl module

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aab86dec-6bc8-4818-860c-f363649a9ba6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds Global CommDomain support across native backends, Python APIs, local and remote L3 workers, MPI groups, lifecycle management, copy operations, smoke tests, documentation, and A5 CI jobs.

Changes

Global CommDomain implementation

Layer / File(s) Summary
Domain contracts and native runtime support
python/simpler/global_comm_domain.py, src/common/platform_comm/*, src/common/worker/*, python/bindings/*
Adds versioned descriptors, codecs, capability checks, native prepare/import/release APIs, simulation and A3 implementations, and Python bindings.
Remote control and session plumbing
python/simpler/remote_l3_session.py, python/simpler/worker.py, src/common/hierarchical/*
Adds remote domain commands, payload staging, manifest validation, phased control handling, descriptor forwarding, copy operations, and cleanup.
Public APIs and MPI coordination
python/simpler/orchestrator.py, python/simpler/task_interface.py, python/simpler/mpi_l3_session.py
Adds domain handles and views, allocation and copy APIs, MPI group coordination, descriptor exchange, and readiness reporting.
Smoke tests and validation
tests/ut/*, tools/a3_l4_tcp_smoke/*, tools/remote_l4_npu/*
Adds unit, TCP, MPI, A3 TLOAD, compute-then-TLOAD, and remote NPU validation flows.
Documentation and CI updates
docs/*, .github/workflows/ci.yml
Documents protocol and implementation status, and updates A5 checkout directories and pytest cache settings.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Orchestrator
  participant Worker
  participant RemoteL3Session
  participant CommRuntime
  Orchestrator->>Worker: allocate_global_domain
  Worker->>RemoteL3Session: prepare and exchange descriptors
  RemoteL3Session->>CommRuntime: prepare/import domain
  CommRuntime-->>RemoteL3Session: descriptor or device context
  RemoteL3Session-->>Worker: commit domain
  Worker-->>Orchestrator: GlobalCommDomainHandle
Loading

Possibly related PRs

Poem

A rabbit hops through domains wide,
With ranked descriptors side by side.
It maps, commits, then copies bright,
And cleans its tunnels before night.
“TLOAD!” it cheers, with ears held high.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: support for mpirun-launched multi-host L3 workers.
Description check ✅ Passed The description directly covers MPI worker groups, Global CommDomains, testing, and validation results.

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.

@sunkaixuan2018
sunkaixuan2018 marked this pull request as ready for review August 1, 2026 08:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Nitpick comments (19)
docs/comm-domain.md (1)

51-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document or link the MPI-launched topology.

The Global CommDomain section only covers forked local workers and TCP-connected workers, calling out that the smoke cases do not use mpirun. If static mpi-launched L4/L3 groups are supported, add the manifest, readiness, and rank-order contract here or link to an authoritative L4/L3 startup section.

🤖 Prompt for 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.

In `@docs/comm-domain.md` around lines 51 - 55, Update the “Global CommDomain
across local and remote L3 nodes” section to document the supported MPI-launched
L4/L3 topology, including its manifest, readiness requirements, and rank-order
contract; if those details are defined elsewhere, link to the authoritative
startup section instead. Keep the existing local fork and TCP-connected worker
coverage intact.
python/simpler/worker.py (2)

1924-1930: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the failed release in the L2 sweep.

_sweep_l2_global_domains runs at chip-child teardown and drops every release error silently. A backend release failure there means a leaked HCCL window, and no diagnostic reaches the parent. Write the first failure to stderr so post-mortems can see it. This also addresses the try-except-pass static-analysis hint.

♻️ Proposed refactor
 def _sweep_l2_global_domains(cw: ChipWorker, store: _L2GlobalDomainStore) -> None:
     for domain_id in list(store.domains):
         store.domains.pop(domain_id, None)
         try:
             cw._impl.comm_global_domain_release(int(domain_id))
-        except Exception:  # noqa: BLE001
-            pass
+        except Exception as exc:  # noqa: BLE001
+            sys.stderr.write(
+                f"_sweep_l2_global_domains: domain_id={domain_id} release failed: {type(exc).__name__}: {exc}\n"
+            )
+            sys.stderr.flush()
🤖 Prompt for 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.

In `@python/simpler/worker.py` around lines 1924 - 1930, Update
_sweep_l2_global_domains to report the first exception from
comm_global_domain_release to stderr while continuing the sweep for remaining
domains; replace the silent except/pass handling with a concise diagnostic that
includes the failure details.

Source: Linters/SAST tools


3683-3701: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make _close_mpirun_groups tolerant of one failing group.

Two problems exist in this loop:

  1. If the final proc.wait(timeout=timeout_s) after proc.kill() raises TimeoutExpired, the exception leaves the loop. Later groups keep their process and ready_dir, and the temporary ready directories leak.
  2. On the rollback path the first call is proc.wait(timeout=timeout_s). A hung mpirun therefore consumes the whole grace budget per group before terminate() runs.

Collect per-group errors and always remove the ready directory.

♻️ Proposed refactor
     def _close_mpirun_groups(self, *, timeout_s: float = _ROLLBACK_GRACEFUL_TIMEOUT_S) -> None:
+        errors: list[BaseException] = []
         for group in reversed(self._mpi_l3_groups):
             proc = group.process
             if proc is not None:
                 try:
                     proc.wait(timeout=timeout_s)
                 except subprocess.TimeoutExpired:
                     proc.terminate()
                     try:
                         proc.wait(timeout=timeout_s)
                     except subprocess.TimeoutExpired:
                         proc.kill()
-                        proc.wait(timeout=timeout_s)
+                        try:
+                            proc.wait(timeout=timeout_s)
+                        except BaseException as exc:  # noqa: BLE001
+                            errors.append(exc)
                 finally:
                     group.process = None
             if group.ready_dir is not None:
                 shutil.rmtree(group.ready_dir, ignore_errors=True)
                 group.ready_dir = None
                 group.manifest_path = None
+        if errors:
+            raise errors[0]
🤖 Prompt for 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.

In `@python/simpler/worker.py` around lines 3683 - 3701, Update
_close_mpirun_groups to isolate failures per group: ensure every cleanup
attempt, including the wait after proc.kill(), is caught and recorded rather
than escaping the loop, then continue processing remaining groups. On the
rollback path, avoid spending the full timeout before terminate() by using the
intended immediate or bounded termination sequence, while always clearing
group.process and removing ready_dir/manifest_path in finally-style cleanup.
src/common/hierarchical/remote_endpoint.cpp (1)

937-941: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Move result_bytes out of the reply.

run_control returns a ControlReplyPayload by value. Accessing .result_bytes on that temporary copies the vector. COPY_FROM_DOMAIN results reach GLOBAL_DOMAIN_MAX_COPY_BYTES (8 MiB), so this adds one full buffer copy per domain read. Move the member out of the temporary instead.

⚡ Proposed fix
 std::vector<uint8_t> RemoteL3Endpoint::control_remote_domain(
     remote_l3::ControlName control_name, const std::vector<uint8_t> &command_bytes
 ) {
-    return run_control(control_name, command_bytes).result_bytes;
+    return std::move(run_control(control_name, command_bytes).result_bytes);
 }

Run clang-format -i src/common/hierarchical/remote_endpoint.cpp after the change.

As per coding guidelines: "Run clang-format -i <file> when formatting C++ code."

🤖 Prompt for 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.

In `@src/common/hierarchical/remote_endpoint.cpp` around lines 937 - 941, Update
RemoteL3Endpoint::control_remote_domain to move result_bytes from the temporary
ControlReplyPayload returned by run_control instead of copying it. Preserve the
existing return type and behavior, then run clang-format on the file.

Source: Coding guidelines

python/simpler/global_comm_domain.py (1)

371-386: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify decode_comm_init_result to one construction.

The function builds GlobalCommInitResult twice only to order the reads. Read the fields into locals first, then construct once. The read order stays identical.

♻️ Proposed refactor
 def decode_comm_init_result(data: bytes) -> GlobalCommInitResult:
     reader = _Reader(data)
-    result = GlobalCommInitResult(
-        profile="",
-        max_ranks=reader.u32(),
-        descriptor_bytes=reader.u32(),
-        local_device_count=reader.u32(),
-    )
-    result = GlobalCommInitResult(
-        profile=reader.string("profile"),
-        max_ranks=result.max_ranks,
-        descriptor_bytes=result.descriptor_bytes,
-        local_device_count=result.local_device_count,
-    )
+    max_ranks = reader.u32()
+    descriptor_bytes = reader.u32()
+    local_device_count = reader.u32()
+    profile = reader.string("profile")
     reader.done("COMM_INIT result")
-    return result
+    return GlobalCommInitResult(
+        profile=profile,
+        max_ranks=max_ranks,
+        descriptor_bytes=descriptor_bytes,
+        local_device_count=local_device_count,
+    )
🤖 Prompt for 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.

In `@python/simpler/global_comm_domain.py` around lines 371 - 386, Update
decode_comm_init_result to read profile, max_ranks, descriptor_bytes, and
local_device_count into local variables in the existing wire order, then
construct GlobalCommInitResult exactly once with those values; keep
reader.done("COMM_INIT result") and the returned result unchanged.
python/simpler/task_interface.py (1)

1096-1106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding a __repr__ for parity with CommDomainHandle.

CommDomainHandle.__repr__ reports the live/released/freed state, which helps when a close() residual is logged. GlobalCommDomainHandle has no __repr__, so Worker._describe_live_resources diagnostics and stderr messages show the default object repr for these handles.

♻️ Proposed addition
     def __exit__(self, *_):
         self.release()
+
+    def __repr__(self) -> str:
+        if self._freed:
+            state = "freed"
+        elif self._released:
+            state = "released-pending-free"
+        else:
+            state = "live"
+        return (
+            f"GlobalCommDomainHandle(name={self.name!r}, domain_id={self.domain_id}, "
+            f"ranks={len(self.members)}, {state})"
+        )
🤖 Prompt for 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.

In `@python/simpler/task_interface.py` around lines 1096 - 1106, Add a __repr__
method to GlobalCommDomainHandle matching CommDomainHandle’s representation,
including the handle’s live, released, and freed state. Ensure
Worker._describe_live_resources and stderr diagnostics display useful state
information instead of the default object representation.
python/simpler/remote_l3_worker.py (1)

67-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the accepted profiles from GLOBAL_DOMAIN_PROFILE_IDS.

The tuple ("sim", "a3-fabric-v1") and the a3-fabric-v1 platform rule are repeated here and in python/simpler/worker.py (RemoteWorkerSpec.__post_init__, MpiL3GroupSpec.__post_init__, and Worker._validate_global_node_config). A new profile must then be added in four places, and one missed copy makes the daemon reject a manifest the parent considers valid. Import the profile table and check membership against it.

♻️ Proposed refactor
-    comm_profile = str(manifest.get("comm_profile", manifest["transport"]))
-    if comm_profile not in ("sim", "a3-fabric-v1"):
+    comm_profile = str(manifest.get("comm_profile", manifest["transport"]))
+    if comm_profile not in GLOBAL_DOMAIN_PROFILE_IDS:
         raise ValueError("manifest comm_profile is not supported")
-    if comm_profile == "a3-fabric-v1" and not str(manifest["platform"]).startswith("a2a3"):
+    if comm_profile == GLOBAL_DOMAIN_PROFILE_A3_FABRIC and not str(manifest["platform"]).startswith("a2a3"):
         raise ValueError("manifest a3-fabric-v1 comm_profile requires an a2a3 platform")
-    if comm_profile == "a3-fabric-v1" and str(manifest["platform"]).endswith("sim"):
+    if comm_profile == GLOBAL_DOMAIN_PROFILE_A3_FABRIC and str(manifest["platform"]).endswith("sim"):
         raise ValueError("manifest a3-fabric-v1 comm_profile requires real A3 devices")

Add the import at the top of the module:

from .global_comm_domain import GLOBAL_DOMAIN_PROFILE_A3_FABRIC, GLOBAL_DOMAIN_PROFILE_IDS
🤖 Prompt for 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.

In `@python/simpler/remote_l3_worker.py` around lines 67 - 73, Update manifest
validation around comm_profile to import and use GLOBAL_DOMAIN_PROFILE_IDS from
global_comm_domain instead of the hard-coded profile tuple, while preserving the
existing a3-fabric-v1 platform constraints via the shared
GLOBAL_DOMAIN_PROFILE_A3_FABRIC definition.
tools/remote_l4_npu/start_machine_daemon.sh (1)

14-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SIMPLER_REMOTE_L4_NPU_ROLE is only printed, never used.

The script defaults the variable, echoes it, and then never passes it to python -m simpler.remote_l3_worker. An operator who sets SIMPLER_REMOTE_L4_NPU_ROLE gets no behavior change. Either forward it to the daemon or drop the variable and the echo.

Also applies to: 21-26

🤖 Prompt for 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.

In `@tools/remote_l4_npu/start_machine_daemon.sh` at line 14, Update
start_machine_daemon.sh so SIMPLER_REMOTE_L4_NPU_ROLE affects the python -m
simpler.remote_l3_worker invocation by forwarding the configured role through
the daemon’s supported argument or environment mechanism; otherwise remove the
unused default and echo. Ensure an operator-provided role changes daemon
behavior and retain only relevant logging.
python/simpler/orchestrator.py (1)

449-450: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add docstrings to the new public copy and release APIs.

allocate_global_domain and get_global_domain document their contract. release_global_domain, copy_to_global_domain, and copy_from_global_domain do not. These three methods carry non-obvious semantics that a caller cannot infer from the signature: domain_rank selects the target rank inside the domain, buffer selects a named carve-out, and offset is relative to that buffer when buffer is given and relative to the mapped window otherwise.

Document that offset semantics on each method.

Also applies to: 473-500

🤖 Prompt for 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.

In `@python/simpler/orchestrator.py` around lines 449 - 450, て
src/common/hierarchical/worker.h (1)

183-187: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider naming this forwarder control_remote_domain.

Every neighboring forwarder uses the control_* prefix and keeps the manager's name (control_prepare, control_alloc_domain, control_payload). This method inverts the words while forwarding to manager_.control_remote_domain. Aligning the name keeps the control surface consistent. Update the Python binding site together with the rename.

🤖 Prompt for 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.

In `@src/common/hierarchical/worker.h` around lines 183 - 187, Rename the worker
forwarder method from remote_domain_control to control_remote_domain, preserving
its delegation to manager_.control_remote_domain. Update the corresponding
Python binding site and any references to use the renamed method consistently.
src/common/platform_comm/comm_sim.cpp (1)

810-838: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider unmapping the peer mappings that this import already created when it fails.

A mid-loop shm_open or mmap failure returns -1 with allocation->peer_mappings partly filled and host_ctx still null. GlobalDomainAllocation cleans these up at release, so no mapping leaks permanently. However, a caller that retries comm_global_domain_import for the same domain_id passes the host_ctx != nullptr gate again and appends a second set of peer mappings for the same windows, so the process holds duplicate mappings until release. Rolling back the mappings added by the failed attempt keeps the retry path allocation-neutral.

♻️ Proposed rollback on the import failure path
     auto ctx = std::make_unique<CommContext>();
     ctx->rankId = allocation->rank;
     ctx->rankNum = allocation->nranks;
     ctx->winSize = allocation->mapping_size;
+    const size_t mappings_before = allocation->peer_mappings.size();
+    auto rollback = [&]() {
+        for (size_t i = mappings_before; i < allocation->peer_mappings.size(); ++i) {
+            munmap(allocation->peer_mappings[i].base, allocation->peer_mappings[i].size);
+        }
+        allocation->peer_mappings.resize(mappings_before);
+    };
     allocation->peer_mappings.reserve(allocation->nranks - 1);
     for (uint32_t rank = 0; rank < allocation->nranks; ++rank) {
         const auto *descriptor = rank_order[rank];
         if (descriptor == nullptr) {
+            rollback();
             return -1;
         }
         void *base = allocation->local_base;
         if (rank != allocation->rank) {
             std::string peer_name(
                 reinterpret_cast<const char *>(descriptor->handle), static_cast<size_t>(descriptor->handle_size)
             );
             int fd = shm_open(peer_name.c_str(), O_RDWR, 0600);
             if (fd < 0) {
+                rollback();
                 return -1;
             }
             base = mmap(nullptr, allocation->mapping_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
             close(fd);
             if (base == MAP_FAILED) {
+                rollback();
                 return -1;
             }
🤖 Prompt for 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.

In `@src/common/platform_comm/comm_sim.cpp` around lines 810 - 838, On every
failure path inside the peer-mapping loop in comm_global_domain_import, unmap
and remove the GlobalPeerMapping entries created during the current import
attempt before returning -1. Cover descriptor validation, shm_open failure, and
mmap failure, preserving pre-existing mappings so retries do not append
duplicates.
tools/a3_l4_tcp_smoke/global_tload_smoke.py (1)

143-152: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Release the global domain in a finally block in both read callbacks. Both read-and-release callbacks call domain.release() after the read loop with no try/finally. If copy_from_global_domain raises, the release does not run. Worker.close() still tears the domain down, so no leak occurs, but the two scripts diverge from the verify_phase pattern in tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py at lines 237-248.

  • tools/a3_l4_tcp_smoke/global_tload_smoke.py#L143-L152: wrap the for rank in range(len(node_ids)) read loop in try and move domain.release() into a finally block.
  • tools/a3_l4_tcp_smoke/mixed_global_tload_smoke.py#L164-L173: wrap the for rank in range(node_count) read loop in try and move domain.release() into a finally block.
🤖 Prompt for 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.

In `@tools/a3_l4_tcp_smoke/global_tload_smoke.py` around lines 143 - 152, Ensure
both read callbacks release their global domains in finally blocks: in
tools/a3_l4_tcp_smoke/global_tload_smoke.py lines 143-152, wrap the rank read
loop in read_and_release with try/finally and move domain.release() into
finally; apply the same change to
tools/a3_l4_tcp_smoke/mixed_global_tload_smoke.py lines 164-173 around its
range(node_count) loop. Preserve the existing read and append behavior.
tools/remote_l4_npu/remote_l4_npu_smoke.py (2)

273-278: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Log the ignored remote_free failures.

The bare except Exception: pass hides every teardown error. Print the failure so an operator can see a leaked remote buffer. Cleanup still continues for the remaining handles.

♻️ Proposed change
         for handle in reversed(remote_buffers):
             try:
                 worker.remote_free(handle)
-            except Exception:  # noqa: BLE001
-                pass
+            except Exception as error:  # noqa: BLE001
+                print(f"[remote-l4-group] remote_free failed: {error}")
🤖 Prompt for 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.

In `@tools/remote_l4_npu/remote_l4_npu_smoke.py` around lines 273 - 278, Update
the remote buffer cleanup loop around worker.remote_free to catch each exception
and log the failure, including the affected handle and error details, while
continuing to process remaining handles; preserve the subsequent worker.close()
call.

Source: Linters/SAST tools


41-41: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

The shared keepalive slot drops references on a second invocation.

_REMOTE_GROUP_KEEPALIVE[:] = [...] replaces the previous contents. The comment states the inner Worker drains after the callback returns, so serial invocation is safe. If the remote L3 ever runs a second remote_l3_group_orch task before the first drains, the slice assignment releases the first TaskArgs while the native side may still read it. That failure is a use-after-free, and it is silent. Append instead of replacing, and clear the list only after the drain completes.

🛡️ Proposed change
-    _REMOTE_GROUP_KEEPALIVE[:] = [chip_args0, chip_args1]
+    _REMOTE_GROUP_KEEPALIVE.extend((chip_args0, chip_args1))
     orch.submit_next_level_group(chip_handle, [chip_args0, chip_args1], cfg, workers=[0, 1])

Also applies to: 66-69

🤖 Prompt for 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.

In `@tools/remote_l4_npu/remote_l4_npu_smoke.py` at line 41, Update the keepalive
handling around _REMOTE_GROUP_KEEPALIVE and remote_l3_group_orch so each
invocation appends its TaskArgs reference instead of replacing existing entries.
Retain all references while native workers may still read them, and clear the
shared list only after the inner Worker callback has completed and the drain is
guaranteed.
tests/ut/py/test_global_comm_domain.py (2)

744-745: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider capturing daemon output for failure diagnosis.

Both tests discard daemon stdout and stderr. If a daemon fails to start, _wait_for_tcp_ports raises TimeoutError with no cause. Capture the streams to a pipe or temporary file and print them when startup times out.

Also applies to: 822-823

🤖 Prompt for 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.

In `@tests/ut/py/test_global_comm_domain.py` around lines 744 - 745, Update both
daemon startup test paths around the subprocess stdout/stderr configuration to
capture output instead of discarding it, then include the collected stdout and
stderr in the diagnostic output when _wait_for_tcp_ports raises TimeoutError.
Preserve normal startup behavior and ensure the captured streams are available
for both referenced test cases.

665-666: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Annotate buffer_ptrs as ClassVar to satisfy RUF012.

Ruff flags the mutable dict class attribute. Add a ClassVar annotation to keep the lint step clean.

♻️ Proposed change
     class FakeContext:
-        buffer_ptrs = {"lhs": 0x1000, "rhs": 0x2000, "input": 0x3000}
+        buffer_ptrs: ClassVar[dict[str, int]] = {"lhs": 0x1000, "rhs": 0x2000, "input": 0x3000}

Add the import at the top of the file:

+from typing import ClassVar
🤖 Prompt for 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.

In `@tests/ut/py/test_global_comm_domain.py` around lines 665 - 666, Update
FakeContext.buffer_ptrs by annotating the mutable class-level dictionary as
ClassVar, and add the corresponding typing import at the file level so RUF012
passes without changing its values or behavior.

Source: Linters/SAST tools

tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py (1)

117-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document this smoke in the tool README.

tools/a3_l4_tcp_smoke/README.md describes global_tload_smoke.py, mixed_global_tload_smoke.py, and compute_then_tload_smoke.py. It does not describe this mpirun 2x2 script. Add a section with the required arguments and the two-host prerequisites.

Do you want me to draft the README section?

🤖 Prompt for 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.

In `@tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py` around lines
117 - 121, Update tools/a3_l4_tcp_smoke/README.md to document
mpirun_compute_then_tload_2x2_smoke.py, including its required command-line
arguments and the prerequisites for running across two hosts. Follow the
structure and terminology of the existing smoke sections and describe the
two-host setup requirements.
tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py (2)

46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use iterable unpacking instead of list concatenation.

Ruff reports RUF005 here. The same pattern appears in tools/a3_l4_tcp_smoke/global_tload_smoke.py and tools/remote_l4_npu/remote_l4_npu_smoke.py.

♻️ Proposed change
-    kernel_include_dirs = list(include_dirs) + [str(compiler.project_root / "src" / "common")]
+    kernel_include_dirs = [*include_dirs, str(compiler.project_root / "src" / "common")]
🤖 Prompt for 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.

In `@tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py` at line 46, Update the
include-directory assignments in compute_then_tload_smoke.py,
global_tload_smoke.py, and remote_l4_npu_smoke.py to use iterable unpacking
instead of list concatenation, preserving the existing include_dirs entries and
appended common source directory.

Source: Linters/SAST tools


41-41: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Increase the global domain window to avoid tight carving.

WINDOW_SIZE is 4096 while the four CommBufferSpec entries each request 1024 bytes, so the total is exactly 4096. Buffer carving is sequential, so this leaves no slack for any future alignment/header/metadata padding; add a small extra window margin before the next non-zero buffer and any future allocations.

🤖 Prompt for 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.

In `@tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py` at line 41, Increase the
global WINDOW_SIZE constant above the current 4096-byte total so the four
1024-byte CommBufferSpec allocations have slack for alignment, metadata, and
future buffers. Preserve the existing buffer definitions and carving behavior.
🤖 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 `@docs/comm-domain.md`:
- Around line 90-92: Update the documentation sentence around
GlobalCommDomainHandle.buffer_range() and _global_copy_range() to distinguish
their bounds sources: named-buffer offset and limit calculations use each
buffer’s nbytes, while unbuffered mapped-window validation uses the returned
mapping_size. Keep the existing statements about backend-reported mapped size
and A3 Fabric alignment intact.
- Around line 92-95: Resolve the contradiction between the ChipDomainContext API
fields and the statement that device pointers never cross the public Python API:
update the wording near orch.get_global_domain(domain_id) to limit the
no-pointer claim specifically to the L4 GlobalCommDomainHandle, or revise the
earlier ChipDomainContext API table so both sections describe the same
public-pointer contract.

In `@docs/remote-l3-worker-design/protocol.md`:
- Around line 315-318: Clarify the RELEASE_DOMAIN description by distinguishing
the release request from physical deallocation: state when the handle is marked
released, that backend teardown is deferred until the run fence, and how this
applies to retain_after_run allocations, explicit release, and session shutdown.
Align the wording with the release() semantics documented in comm-domain.md.

In `@python/bindings/worker_bind.h`:
- Around line 706-716: Update the worker control binding lambda before
constructing command_bytes or calling remote_domain_control to validate
control_name with remote_l3::valid_control_name(control_name). Reject invalid
values at the binding boundary, and only perform the static_cast to
remote_l3::ControlName after validation.

In `@python/simpler/mpi_l3_session.py`:
- Around line 48-118: Update Worker._global_domain_control_many and
prepare_import so fan-out failures or missing ranks trigger a bounded timeout
and cancellation/cleanup of outstanding ALLOC_DOMAIN work and MPI collective
progress. Ensure a failed allgather cannot leave other ranks blocked
indefinitely, and unwind prepared global-domain state before propagating the
failure to _close_mpirun_groups.

In `@python/simpler/remote_l3_session.py`:
- Around line 698-714: Update the REMOTE_DEVICE allocation path in
_RemoteBufferEntry handling so EXPORT_BUFFER can safely process
HostBuffer-backed entries. Resolve the backing shared-memory name for HostBuffer
instances before the entry.shm_name.encode path, or explicitly reject the
allocation as non-exportable before reaching it; preserve direct SharedMemory
export behavior.

In `@python/simpler/worker.py`:
- Around line 564-570: Update the zip call in the validation loop over
global_device_ranks_by_rank and device_ids_by_rank to pass strict=True,
preserving the existing iteration and validation behavior while resolving Ruff
B905.
- Around line 1764-1790: Wrap the GlobalDomainDescriptor.decode call in the
global-domain preparation flow with cleanup that invokes
cw._impl.comm_global_domain_release(int(domain_id)) when decoding raises,
matching the existing inconsistent-descriptor release path. Ensure the exception
still propagates and that successful decoding continues into the descriptor
validation and store.domains insertion unchanged.

In `@src/a2a3/platform/onboard/host/comm_hccl.cpp`:
- Around line 1636-1660: Update the import failure path in the peer-window loop
to release and clear all previously imported entries in allocation->peer_windows
before returning -1. Ensure retries through _handle_ctrl_global_domain_import do
not retain or append duplicate mappings, while preserving the existing error
logging and successful import behavior.

In `@tests/ut/py/test_global_comm_domain.py`:
- Around line 704-707: Replace the single-port helper _free_tcp_port with a
helper that reserves and returns the requested number of distinct ephemeral
ports while keeping all sockets open until every port is selected; update the
call sites to request two ports together where both daemons are configured and
one port for the single-port case, preserving existing port usage.

In `@tools/a3_l4_tcp_smoke/kernels/aiv/global_tload_kernel.cpp`:
- Around line 22-34: Update the attribute on CommRemotePtr to use the already
guarded __aicore__ spelling, or define AICORE as a fallback alongside the
existing __aicore__ guard before the function. Ensure CommRemotePtr and
kernel_entry use a consistently available accelerator-core attribute without
changing the function’s behavior.

In `@tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py`:
- Around line 37-41: Update _parse_csv_ints to reject any negative device ID and
reject duplicate IDs, raising a clear ValueError before returning the parsed
tuple; preserve acceptance of non-empty, unique non-negative integers and apply
the same validation wherever this helper is used.
- Line 10: Replace the specific lab machine names "35/37" in the module
docstring with a generic description of the two-host layout. Remove the
hardcoded default values for host and RoCE IP addresses in the argument parser
setup (around lines 214-219), make those arguments required instead, and update
all attribute name references in the run function from specific machine
identifiers (args.host_37, args.host_35) to generic placeholders (args.host_a,
args.host_b). This removes private network topology details from the code while
maintaining the two-host computation pattern as the core functionality.

In `@tools/a3_l4_tcp_smoke/README.md`:
- Line 7: Update the README description to use the fabric profile name
“a3-fabric-v1” instead of “Fabric V2,” matching the comm_profile requested by
the smoke scripts.

In `@tools/remote_l4_npu/remote_l4_npu_smoke.py`:
- Line 177: Update the argument definition for --session-listen-host to require
an explicitly supplied host instead of defaulting to 0.0.0.0. In the Worker
construction calls around the session listener setup, derive
allow_wildcard_session_bind from whether the supplied host is a wildcard
address, enabling it only for an explicitly requested wildcard and preserving it
as disabled for specific reachable addresses.

In `@tools/remote_l4_npu/start_machine_daemon.sh`:
- Line 15: Change the default value of SIMPLER_REMOTE_L4_NPU_HOST in
start_machine_daemon.sh from 0.0.0.0 to 127.0.0.1, preserving explicit
environment overrides for users who intentionally need a routable bind address.

---

Nitpick comments:
In `@docs/comm-domain.md`:
- Around line 51-55: Update the “Global CommDomain across local and remote L3
nodes” section to document the supported MPI-launched L4/L3 topology, including
its manifest, readiness requirements, and rank-order contract; if those details
are defined elsewhere, link to the authoritative startup section instead. Keep
the existing local fork and TCP-connected worker coverage intact.

In `@python/simpler/global_comm_domain.py`:
- Around line 371-386: Update decode_comm_init_result to read profile,
max_ranks, descriptor_bytes, and local_device_count into local variables in the
existing wire order, then construct GlobalCommInitResult exactly once with those
values; keep reader.done("COMM_INIT result") and the returned result unchanged.

In `@python/simpler/orchestrator.py`:
- Around line 449-450: て

In `@python/simpler/remote_l3_worker.py`:
- Around line 67-73: Update manifest validation around comm_profile to import
and use GLOBAL_DOMAIN_PROFILE_IDS from global_comm_domain instead of the
hard-coded profile tuple, while preserving the existing a3-fabric-v1 platform
constraints via the shared GLOBAL_DOMAIN_PROFILE_A3_FABRIC definition.

In `@python/simpler/task_interface.py`:
- Around line 1096-1106: Add a __repr__ method to GlobalCommDomainHandle
matching CommDomainHandle’s representation, including the handle’s live,
released, and freed state. Ensure Worker._describe_live_resources and stderr
diagnostics display useful state information instead of the default object
representation.

In `@python/simpler/worker.py`:
- Around line 1924-1930: Update _sweep_l2_global_domains to report the first
exception from comm_global_domain_release to stderr while continuing the sweep
for remaining domains; replace the silent except/pass handling with a concise
diagnostic that includes the failure details.
- Around line 3683-3701: Update _close_mpirun_groups to isolate failures per
group: ensure every cleanup attempt, including the wait after proc.kill(), is
caught and recorded rather than escaping the loop, then continue processing
remaining groups. On the rollback path, avoid spending the full timeout before
terminate() by using the intended immediate or bounded termination sequence,
while always clearing group.process and removing ready_dir/manifest_path in
finally-style cleanup.

In `@src/common/hierarchical/remote_endpoint.cpp`:
- Around line 937-941: Update RemoteL3Endpoint::control_remote_domain to move
result_bytes from the temporary ControlReplyPayload returned by run_control
instead of copying it. Preserve the existing return type and behavior, then run
clang-format on the file.

In `@src/common/hierarchical/worker.h`:
- Around line 183-187: Rename the worker forwarder method from
remote_domain_control to control_remote_domain, preserving its delegation to
manager_.control_remote_domain. Update the corresponding Python binding site and
any references to use the renamed method consistently.

In `@src/common/platform_comm/comm_sim.cpp`:
- Around line 810-838: On every failure path inside the peer-mapping loop in
comm_global_domain_import, unmap and remove the GlobalPeerMapping entries
created during the current import attempt before returning -1. Cover descriptor
validation, shm_open failure, and mmap failure, preserving pre-existing mappings
so retries do not append duplicates.

In `@tests/ut/py/test_global_comm_domain.py`:
- Around line 744-745: Update both daemon startup test paths around the
subprocess stdout/stderr configuration to capture output instead of discarding
it, then include the collected stdout and stderr in the diagnostic output when
_wait_for_tcp_ports raises TimeoutError. Preserve normal startup behavior and
ensure the captured streams are available for both referenced test cases.
- Around line 665-666: Update FakeContext.buffer_ptrs by annotating the mutable
class-level dictionary as ClassVar, and add the corresponding typing import at
the file level so RUF012 passes without changing its values or behavior.

In `@tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py`:
- Line 46: Update the include-directory assignments in
compute_then_tload_smoke.py, global_tload_smoke.py, and remote_l4_npu_smoke.py
to use iterable unpacking instead of list concatenation, preserving the existing
include_dirs entries and appended common source directory.
- Line 41: Increase the global WINDOW_SIZE constant above the current 4096-byte
total so the four 1024-byte CommBufferSpec allocations have slack for alignment,
metadata, and future buffers. Preserve the existing buffer definitions and
carving behavior.

In `@tools/a3_l4_tcp_smoke/global_tload_smoke.py`:
- Around line 143-152: Ensure both read callbacks release their global domains
in finally blocks: in tools/a3_l4_tcp_smoke/global_tload_smoke.py lines 143-152,
wrap the rank read loop in read_and_release with try/finally and move
domain.release() into finally; apply the same change to
tools/a3_l4_tcp_smoke/mixed_global_tload_smoke.py lines 164-173 around its
range(node_count) loop. Preserve the existing read and append behavior.

In `@tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py`:
- Around line 117-121: Update tools/a3_l4_tcp_smoke/README.md to document
mpirun_compute_then_tload_2x2_smoke.py, including its required command-line
arguments and the prerequisites for running across two hosts. Follow the
structure and terminology of the existing smoke sections and describe the
two-host setup requirements.

In `@tools/remote_l4_npu/remote_l4_npu_smoke.py`:
- Around line 273-278: Update the remote buffer cleanup loop around
worker.remote_free to catch each exception and log the failure, including the
affected handle and error details, while continuing to process remaining
handles; preserve the subsequent worker.close() call.
- Line 41: Update the keepalive handling around _REMOTE_GROUP_KEEPALIVE and
remote_l3_group_orch so each invocation appends its TaskArgs reference instead
of replacing existing entries. Retain all references while native workers may
still read them, and clear the shared list only after the inner Worker callback
has completed and the drain is guaranteed.

In `@tools/remote_l4_npu/start_machine_daemon.sh`:
- Line 14: Update start_machine_daemon.sh so SIMPLER_REMOTE_L4_NPU_ROLE affects
the python -m simpler.remote_l3_worker invocation by forwarding the configured
role through the daemon’s supported argument or environment mechanism; otherwise
remove the unused default and echo. Ensure an operator-provided role changes
daemon behavior and retain only relevant logging.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 29a64aec-682b-40c2-90d0-c135fd63ab80

📥 Commits

Reviewing files that changed from the base of the PR and between 4d735ce and 491f69c.

📒 Files selected for processing (46)
  • .github/workflows/ci.yml
  • docs/comm-domain.md
  • docs/remote-l3-worker-design.md
  • docs/remote-l3-worker-design/implementation-record.md
  • docs/remote-l3-worker-design/protocol.md
  • python/bindings/CMakeLists.txt
  • python/bindings/task_interface.cpp
  • python/bindings/worker_bind.h
  • python/simpler/global_comm_domain.py
  • python/simpler/global_comm_smoke.py
  • python/simpler/mpi_l3_session.py
  • python/simpler/orchestrator.py
  • python/simpler/remote_l3_protocol.py
  • python/simpler/remote_l3_session.py
  • python/simpler/remote_l3_worker.py
  • python/simpler/task_interface.py
  • python/simpler/worker.py
  • src/a2a3/platform/onboard/host/comm_hccl.cpp
  • src/a5/platform/onboard/host/comm_hccl.cpp
  • src/common/hierarchical/remote_endpoint.cpp
  • src/common/hierarchical/remote_endpoint.h
  • src/common/hierarchical/remote_wire.cpp
  • src/common/hierarchical/remote_wire.h
  • src/common/hierarchical/worker.h
  • src/common/hierarchical/worker_manager.cpp
  • src/common/hierarchical/worker_manager.h
  • src/common/platform_comm/comm.h
  • src/common/platform_comm/comm_sim.cpp
  • src/common/worker/chip_worker.cpp
  • src/common/worker/chip_worker.h
  • tests/ut/cpp/CMakeLists.txt
  • tests/ut/py/test_callable_identity.py
  • tests/ut/py/test_global_comm_domain.py
  • tools/a3_l4_tcp_smoke/README.md
  • tools/a3_l4_tcp_smoke/compute_then_tload_smoke.py
  • tools/a3_l4_tcp_smoke/global_tload_smoke.py
  • tools/a3_l4_tcp_smoke/kernels/aiv/global_tload_kernel.cpp
  • tools/a3_l4_tcp_smoke/kernels/aiv/local_add_kernel.cpp
  • tools/a3_l4_tcp_smoke/kernels/orchestration/global_tload_orch.cpp
  • tools/a3_l4_tcp_smoke/kernels/orchestration/local_add_orch.cpp
  • tools/a3_l4_tcp_smoke/mixed_global_tload_smoke.py
  • tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py
  • tools/remote_l4_npu/README.md
  • tools/remote_l4_npu/remote_l4_npu_smoke.py
  • tools/remote_l4_npu/run_parent_smoke.sh
  • tools/remote_l4_npu/start_machine_daemon.sh

Comment thread docs/comm-domain.md Outdated
Comment thread docs/comm-domain.md Outdated
Comment thread docs/remote-l3-worker-design/protocol.md Outdated
Comment thread python/bindings/worker_bind.h
Comment thread python/simpler/mpi_l3_session.py
Comment thread tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py Outdated
Comment thread tools/a3_l4_tcp_smoke/mpirun_compute_then_tload_2x2_smoke.py Outdated
Comment thread tools/a3_l4_tcp_smoke/README.md Outdated
Comment thread tools/remote_l4_npu/remote_l4_npu_smoke.py Outdated
Comment thread tools/remote_l4_npu/start_machine_daemon.sh Outdated
@sunkaixuan2018
sunkaixuan2018 force-pushed the add-mpi-run branch 3 times, most recently from 90cba14 to 48281af Compare August 1, 2026 09:42
@sunkaixuan2018

Copy link
Copy Markdown
Contributor Author

@coderabbitai Addressed the review-body follow-ups in the single squashed commit:

  • documented the MPI topology/member contract and public Global CommDomain ownership/bounds semantics;
  • made MPI collectives and L4 fanout deadline-bounded with partial-state cleanup;
  • isolated mpirun group cleanup failures and added diagnostics;
  • logged L2 sweep release failures and preserved cleanup across entries;
  • simplified result movement and COMM_INIT decoding, added handle repr/docstrings, reused shared profile constants, and clarified the C++ control-forwarder name;
  • made A3 and sim peer mapping imports rollback-safe;
  • captured daemon stdout/stderr and reserved test ports together;
  • fixed the UT-compatible ephemeral listener call while retaining fixed MPI ports.

The remaining review-body notes reference tool/test code that is no longer present in the current PR diff, so no code change was needed for those items.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. More reviews will be available in 12 minutes.

Add static MPI-launched L3 groups with rank-0 manifest broadcast and ready coordination, and extend Global CommDomains across mixed local, TCP-remote, and MPI-backed L3 nodes.

Harden the control path with validated control names, bounded MPI collectives and L4 fanout, rollback-safe peer mappings, isolated process cleanup, fixed-port session binding compatibility, diagnostics, documentation, and regression coverage.

Co-authored-by: Leaf-Salix <2503954024@qq.com>

Co-authored-by: xl <21039015+xl1123@users.noreply.github.com>
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.

1 participant