Skip to content

feat(collectives): ring allreduce TPUT push model on HOST builtin + InCore composite (requires PTOAS >= 0.55) - #2280

Open
georgebisbas wants to merge 5 commits into
hw-native-sys:mainfrom
georgebisbas:ring-allreduce-tput-push
Open

feat(collectives): ring allreduce TPUT push model on HOST builtin + InCore composite (requires PTOAS >= 0.55)#2280
georgebisbas wants to merge 5 commits into
hw-native-sys:mainfrom
georgebisbas:ring-allreduce-tput-push

Conversation

@georgebisbas

Copy link
Copy Markdown
Contributor

Summary

Replaces the pull-model engine of the ring allreduce on both rails with a TPUT push model (remote write), enabling O(1) NeighborBarrier on the HOST builtin and eliminating the pull-model NPU memory-ordering gap.

  • HOST builtin (builtin.tensor.allreduce_ring): reduce-scatter + allgather converted from TLOAD/TSTORE pull to pto::comm::TPUT push — TPUT<AtomicAdd> remote-accumulate for RS, non-atomic TPUT for AG. Ordering is pipe_barrier(PIPE_ALL) around every transfer + dsb(DSB_DDR) before TNOTIFY (mirrors the in-tree allgather/all_to_all host builtins; not a GM fence). The O(P²) RoundBarrier is replaced by the O(1) NeighborBarrier (notify/wait the two ring neighbours only), which is NPU-safe because the TPUT write pipeline orders the data ahead of the signal — the pull model could not provide that.
  • InCore composite (LowerTensorRingAllReduceRule): replaces pld.tile.remote_load pulls with pld.tile.put pushes (non-atomic TPUT + local reduce, preserving Sum/Max/Min/Prod). Race-free per-subchunk protocol: own-value read → ready barrier → push to right neighbour → push-done barrier → local read+reduce+store; barrier credits stay 2 per subchunk (signal shape unchanged). Ragged/arbitrary lengths and FP16 are preserved via balanced segments + valid shapes, with the shared VEC staging tile narrowed per transfer via tile.set_validshape.

⚠️ Requires PTOAS >= v0.55

This PR depends on PTOAS v0.55 (release: hw-native-sys/PTOAS#1069, fixed in PR #1079).

The InCore composite's pld.tile.put transfers carry the exact ragged valid_cols as the partition-view extent. PTOAS ≤ v0.54 rejects dynamic partition-view shapes for pto.comm.tput ('pto.comm.tput' op expects dst to have a positive static shape), so the pure push model cannot compile below v0.55. The HOST builtin does not depend on this (its kernel is hand-written), but the composite rail does.

CI / developers must use PTOAS v0.55+ (install via ptoas-0.55 wheel or ptoas-bin-aarch64.tar.gz, set PTOAS_ROOT accordingly). The UT tests now pin the push structure (pld.tile.put + tile.create staging tile instead of pld.tile.remote_load).

Verification (NPU silicon, 910B2, PTOAS v0.55)

All on real NPUs (8x 910B2), P=2 and P=4:

  • tests/st/distributed/test_l3_host_tensor_allreduce_ring.py — HOST ring, P=2/4 ✅ (with NeighborBarrier enabled)
  • tests/st/distributed/collectives/test_l3_tensor_allreduce_ring_intrinsic.py — InCore ring, P=2/4, sizes {1, 17, 4097, 65537} (ragged + >UB), Sum/Max/Min/Prod, FP16 ✅
  • tests/st/distributed/collectives/test_l3_allreduce_ring.py + test_l3_ring_sizing_prewarm.py — no regression ✅
  • UTs: test_lower_host_tensor_collectives.py, test_host_orch_distributed.py, test_lower_composite_ops.py (+ numerical) all green ✅

Total: 25/25 ST + 190 UT passed.

Trade-off note (ReduceOp)

The HOST builtin is ReduceOp::kSum only by construction, so its TPUT<AtomicAdd> RS is fine. The composite keeps non-atomic push + local reduce to preserve Sum/Max/Min/Prod; only a remote-atomic TPUT<AtomicAdd> variant would be Sum-only (AtomicType has no AtomicMax/Min).

@coderabbitai

coderabbitai Bot commented Aug 4, 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: 95dce9ee-df5d-4769-95c9-f9354a609e8a

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

Changes

Ring allreduce TPUT conversion

Layer / File(s) Summary
Lowering push schedule
src/ir/transforms/lower_composite_ops_pass.cpp, docs/*/dev/passes/12-lower_composite_ops.md
Ring reduce-scatter and allgather now use pld.tile.put with a shared staging tile, dynamic transfer shapes, and ready/push-done barriers.
Kernel push execution and barriers
python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in, docs/*/dev/passes/40-lower_host_tensor_collectives.md
The kernel uses atomic TPUT for reduce-scatter, non-atomic TPUT for allgather, pipe ordering, dsb, and O(1) neighbor barriers.
Lowering validation updates
tests/ut/ir/transforms/test_lower_composite_ops.py
Tests now validate TPUT calls, staging tiles, updated control flow, chunk shapes, FP16 handling, and round-trip output.

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

Sequence Diagram(s)

sequenceDiagram
  participant Rank
  participant NeighborBarrier
  participant RightNeighbor
  Rank->>Rank: Read own receive slot
  Rank->>NeighborBarrier: Wait for ready generation
  Rank->>RightNeighbor: TPUT partial chunk
  Rank->>NeighborBarrier: Wait for push-done generation
  Rank->>Rank: Reduce and store partial
  Rank->>RightNeighbor: TPUT finalized chunk
  Rank->>NeighborBarrier: Wait for allgather completion
Loading

Possibly related PRs

Poem

A rabbit hops where TPUT flows,
Pushes chunks to neighbors close.
Barriers guard each passing byte,
Reduced by day, gathered by night.
Staging tiles keep tails aligned—
Ring allreduce, neatly bind!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the TPUT push-model ring allreduce change across the HOST builtin and InCore composite.
Description check ✅ Passed The description directly explains the push-model changes, synchronization, PTOAS requirement, testing, and supported reduction operations.
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.

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.

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

🧹 Nitpick comments (2)
src/ir/transforms/lower_composite_ops_pass.cpp (1)

1533-1537: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the allgather ready-barrier rationale.

The comment states the ready barrier is safe because "the cumulative generation count includes the previous step's push-done notifies". Each round uses its own signal row (ag_round), so row k's counters start at zero and carry no credit from row k-1. The actual guarantee comes from the previous round's push-done barrier, which every rank must pass before it enters round k. Restate the rationale so a future change does not remove the previous round's push-done barrier on a false premise. The same wording appears at Lines 1571-1573.

🤖 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/ir/transforms/lower_composite_ops_pass.cpp` around lines 1533 - 1537,
Update the ready-barrier rationale comment around lines 1533-1537 to remove the
incorrect explanation about cumulative generation counts including previous
push-done notifies. Replace it with the correct rationale: the guarantee comes
from the previous round's push-done barrier that every rank must pass before
entering round k. Apply the same correction to the identical wording at lines
1571-1573 to ensure consistency and prevent future maintainers from removing the
previous round's push-done barrier based on the false premise.
python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in (1)

121-144: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Add a comment documenting the two-rank NeighborBarrier behavior.

For nranks == 2, both neighbours map to rank 1, so each row cell receives two AtomicAdd writes and is observed twice via the same GE 1 waits. A short comment keeps the two-rank safety invariant explicit; the existing row allocation path already uses a fresh zeroed signal buffer per allreduce call.

🤖 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/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in`
around lines 121 - 144, Add a concise comment above NeighborBarrier documenting
the nranks == 2 case: both neighbours resolve to rank 1, each signal cell
receives two AtomicAdd notifications, and the corresponding GE 1 waits observe
the shared cell twice. Keep the existing implementation unchanged and note that
the signal buffer is freshly zeroed per allreduce call.
🤖 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
`@python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in`:
- Around line 146-149: Update the stale StepBarrier comment near the barrier
configuration to reflect that kUseNeighborBarrier is already enabled and
NeighborBarrier has been verified on 910B2 for P=2 and P=4. Remove the outdated
instructions to land with RoundBarrier first and defer the swap; keep the
comment focused on the current implementation and its verification status.

In `@src/ir/transforms/lower_composite_ops_pass.cpp`:
- Around line 1439-1454: The tile.set_validshape operation returns a new
narrowed TileType and does not mutate put_stage in-place. Update the
pld.tile.put call to pass the narrowed alias rs_stage_valid instead of
put_stage, so the push uses the correct narrowed staging tile extent. Apply the
same fix to the allgather push path by using ag_stage_valid instead of put_stage
in its corresponding pld.tile.put call.

In `@tests/ut/ir/transforms/test_lower_composite_ops.py`:
- Around line 2102-2109: Add a non-empty assertion for the stage_creates
collection in the FP16 test, matching the sibling test’s assertion and message.
Keep the existing chunk_shapes loop and validation unchanged so the test
requires FP16 staging-tile creation before checking alignment and size
constraints.

---

Nitpick comments:
In
`@python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in`:
- Around line 121-144: Add a concise comment above NeighborBarrier documenting
the nranks == 2 case: both neighbours resolve to rank 1, each signal cell
receives two AtomicAdd notifications, and the corresponding GE 1 waits observe
the shared cell twice. Keep the existing implementation unchanged and note that
the signal buffer is freshly zeroed per allreduce call.

In `@src/ir/transforms/lower_composite_ops_pass.cpp`:
- Around line 1533-1537: Update the ready-barrier rationale comment around lines
1533-1537 to remove the incorrect explanation about cumulative generation counts
including previous push-done notifies. Replace it with the correct rationale:
the guarantee comes from the previous round's push-done barrier that every rank
must pass before entering round k. Apply the same correction to the identical
wording at lines 1571-1573 to ensure consistency and prevent future maintainers
from removing the previous round's push-done barrier based on the false premise.
🪄 Autofix

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: 73efeda0-1ee1-418f-ae6b-d8ff15d8b379

📥 Commits

Reviewing files that changed from the base of the PR and between a47d300 and 6e35e57.

📒 Files selected for processing (7)
  • docs/en/dev/passes/12-lower_composite_ops.md
  • docs/en/dev/passes/40-lower_host_tensor_collectives.md
  • docs/zh/dev/passes/12-lower_composite_ops.md
  • docs/zh/dev/passes/40-lower_host_tensor_collectives.md
  • python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in
  • src/ir/transforms/lower_composite_ops_pass.cpp
  • tests/ut/ir/transforms/test_lower_composite_ops.py

Comment thread python/pypto/runtime/builtins/collectives/allreduce_ring/templates/kernel.cpp.in Outdated
Comment thread src/ir/transforms/lower_composite_ops_pass.cpp
Comment thread tests/ut/ir/transforms/test_lower_composite_ops.py Outdated
HOST builtin (builtin.tensor.allreduce_ring):
- Convert reduce-scatter + allgather from TLOAD/TSTORE pull to pto::comm::TPUT
  push: TPUT<AtomicAdd> remote-accumulate for RS, non-atomic TPUT for AG.
- Ordering: pipe_barrier(PIPE_ALL) around every TPUT + dsb(DSB_DDR) before
  TNOTIFY (mirrors the allgather/all_to_all host builtins; NOT a GM fence).
- Enable O(1) NeighborBarrier (notify/wait left+right only) — NPU-safe because
  the TPUT write pipeline orders data ahead of the signal, which the pull model
  lacked. NPU-verified at P=2/4 (both RoundBarrier baseline and NeighborBarrier).

InCore composite (LowerTensorRingAllReduceRule):
- Replace pld.tile.remote_load pulls with pld.tile.put pushes (non-atomic
  TPUT + local reduce, preserving Sum/Max/Min/Prod).
- Race-free protocol: own-value read before the ready barrier, push after it,
  push-done barrier, then local read+reduce+store; credits stay 2 per subchunk.
- Requires PTOAS >= v0.55 (dynamic partition-view shapes for tput, issue hw-native-sys#1069).
- Ragged/arbitrary lengths and FP16 preserved via balanced segments + valid
  shapes; stage tile narrowed with tile.set_validshape per transfer.

Docs: ring builtin lowering notes (en/zh) updated to describe the push model.

NOTE: ring composite UT tests (test_lower_composite_ops.py ring section) still
pin the old pull IR and will be updated in a follow-up commit.
The ring allreduce composite now lowers to pld.tile.put pushes (TPUT) with a
tile.create staging tile instead of pld.tile.remote_load pulls.  Update the
ring UT assertions to pin the push structure:
- required ops: pld.tile.remote_load -> pld.tile.put
- staging tile: tile.create shape [1, chunk_cols] is the UB-bounded aligned
  chunk (stage is created once per ring call)
- puts carry 7 args (dst, peer, src, stage, dst_offsets, src_offsets, shape)
- control flow: if_count 13 -> 14 (the extra If guards each push phase)
- FP16: aligned 16-element stage instead of allow_physical_tail_padding
- round-trip: pld.tile.put print survives parse

All 130 composite UTs pass.
Update the composite lowering pass docs (en/zh): the ring allreduce rule now
moves data with pld.tile.put (TPUT push) instead of pld.tile.remote_load pulls
— own-value read before the ready barrier, push to the right neighbour, push-done
barrier, then local read+reduce+store.  The VEC staging tile is narrowed with
tile.set_validshape per transfer; PTOAS >= v0.55 dynamic partition-view shapes
(hw-native-sys/PTOAS#1069) keep ragged/FP16 tails exact.  Non-atomic push +
local reduce preserves Sum/Max/Min/Prod.
Pre-commit formatting for the TPUT push-model ring changes (matches the
upstream pre-commit CI hooks).
CodeRabbit review: tile.set_validshape returns the narrowed alias (memory
inherited from the input) — bind it and pass it as the pld.tile.put stage in
both the reduce-scatter and allgather pushes so the TPUT single-shot path
reads exactly the transfer's valid width by construction (matches the mesh
store pattern).

Also address review nits:
- kernel.cpp.in: update the stale StepBarrier comment (NeighborBarrier is the
  active, silicon-verified default).
- test: assert stage_creates is non-empty in the FP16 aligned-schedule test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant