Skip to content

[DSV4] perf(tbo): overlap pure-TP all_reduce on TBO + Delay TP#1681

Open
ZhangLirong-amd wants to merge 4 commits into
mainfrom
feat/tp-tbo-ar-overlap
Open

[DSV4] perf(tbo): overlap pure-TP all_reduce on TBO + Delay TP#1681
ZhangLirong-amd wants to merge 4 commits into
mainfrom
feat/tp-tbo-ar-overlap

Conversation

@ZhangLirong-amd

@ZhangLirong-amd ZhangLirong-amd commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Motivation

AITER_BF16_FP8_MOE_BOUND=0 ATOM_MOE_GU_ITLV=1 python -m atom.entrypoints.openai_server   --model=/mnt/m2m_nobackup/lirzhang/DeepSeek-V4-Pro/   --tensor-parallel-size=8  --max-num-batched-tokens=16384   --kv-cache-dtype=fp8   --gpu-memory-utilization=0.85   --enable-tbo

For each 16k prefill, time 567ms -> 495ms, about 15% improve for each prefill

image

Technical Details

Test Plan

Test Result

Submission Checklist

Copilot AI review requested due to automatic review settings July 23, 2026 08:27
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every eligible PR before approval:

  • ✅ Pre Checkin: Black, Ruff, catalog schema validation, non-GPU unit tests

Heavy model tests:

  • ✅ Run after the PR is approved and Pre Checkin passes
  • ✅ Run immediately when an approval review is submitted
  • ✅ Can be requested before approval with labels
Label Tests
ci:full Run all heavy PR model tests: native ATOM, vLLM, and SGLang
ci:atom Run native ATOM model accuracy tests
ci:vllm Run ATOM vLLM OOT model accuracy tests
ci:sglang Run ATOM SGLang model accuracy tests

Heavy jobs are skipped when the PR is not approved and no matching ci:* label is present.
Add labels via the sidebar or gh pr edit 1681 --add-label <label>

@ZhangLirong-amd ZhangLirong-amd changed the title [DSV4] perf(tbo): overlap pure-TP all_reduce on TBO [DSV4] perf(tbo): overlap pure-TP all_reduce on TBO + Delay TP Jul 23, 2026

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

This PR improves DeepSeek-V4 pure-TP + TBO prefill latency by overlapping the TP all-reduce with the partner ubatch’s compute via a new TBO-aware tbo_all_reduce custom op, and enables PrefillDelayer in TP-only + TBO mode to better coalesce prefills into effective ubatches.

Changes:

  • Add a TBO-aware torch.ops.aiter.tbo_all_reduce path that runs TP all-reduce on the TBO comm stream (opt-out via ATOM_TBO_TP_AR_MODE=inline).
  • Route DeepSeek-V4 attention (wo_b) and MoE combine_outputs TP reductions through the TBO-aware custom op in the pure TP+TBO (no DP) case.
  • Enable PrefillDelayer in TP-only + TBO mode (single-rank behavior: no cross-rank all_reduce), and add a benchmark scenario entry for TBO.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
atom/utils/tbo/ubatching.py Adds per-ubatch TP communicators and introduces an (currently unused) event intended for TP AR ordering.
atom/utils/envs.py Updates PrefillDelayer docs; adds ATOM_TBO_TP_AR_MODE and a debug env var entry.
atom/models/deepseek_v4.py Enables TBO-aware TP all-reduce routing for wo_b and MoE combine_outputs in the pure TP+TBO case.
atom/model_ops/module_dispatch_ops.py Registers torch.ops.aiter.tbo_all_reduce custom op implementing overlap behavior under TBO.
atom/model_ops/linear.py Adds tbo_aware flag to RowParallelLinear to route its TP reduction through the new custom op.
atom/model_engine/prefill_delayer.py Adds TP-only (cpu_group=None) behavior and guards the DP all_reduce accordingly.
atom/model_engine/engine_core.py Enables PrefillDelayer automatically in TP-only + TBO mode when allowed by env.
.github/benchmark/models.json Adds a TBO benchmark variant configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +324 to +326
global _TBO_TP_UBATCH_COMMS
if _TBO_TP_UBATCH_COMMS is not None:
return _TBO_TP_UBATCH_COMMS[ubatch_id]
Comment on lines +609 to +612
# Fresh per-step event that serializes the pure-TP all_reduce across
# ubatches (see `_TP_AR_ORDER_EVENT` note above). A new event each step
# means the first ubatch's AR never waits on a prior step's recording.
_TP_AR_ORDER_EVENT = torch.cuda.Event()
Comment thread atom/utils/envs.py Outdated
Comment on lines +384 to +390
# Debug: in split_attn_metadata, cross-check the host-computed per-ubatch
# max_seqlen_q/k against the device tensors and fall back to the device value
# (with a warning) on mismatch. Off by default — turn on to diagnose stale
# attach_tbo_cpu_lens snapshots. Adds two .item() D2H syncs per ubatch.
"ATOM_TBO_DEBUG_CHECK": lambda: (
os.getenv("ATOM_TBO_DEBUG_CHECK", "0") == "1"
),
Copilot AI review requested due to automatic review settings July 23, 2026 08:34

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 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

atom/utils/tbo/ubatching.py:610

  • make_tbo_contexts() allocates a fresh _TP_AR_ORDER_EVENT each step, but _TP_AR_ORDER_EVENT/tbo_get_tp_ar_order_event() are not referenced anywhere in the repo. This adds per-step overhead and is misleading; either wire the event into the pure-TP all_reduce ordering logic or stop creating it until it’s actually used.
    global _NUM_UBATCHES, _CURRENT_CONTEXTS, _TP_AR_ORDER_EVENT
    assert num_micro_batches > 1

    _NUM_UBATCHES = num_micro_batches
    # Grow the global context list if needed
    while len(_CURRENT_CONTEXTS) < num_micro_batches:
        _CURRENT_CONTEXTS.append(None)

    # Fresh per-step event that serializes the pure-TP all_reduce across
    # ubatches (see `_TP_AR_ORDER_EVENT` note above). A new event each step
    # means the first ubatch's AR never waits on a prior step's recording.
    _TP_AR_ORDER_EVENT = torch.cuda.Event()

Comment on lines +324 to +347
global _TBO_TP_UBATCH_COMMS
if _TBO_TP_UBATCH_COMMS is not None:
return _TBO_TP_UBATCH_COMMS[ubatch_id]

from aiter.dist.device_communicators.communicator_pynccl import (
PyNcclCommunicator,
)
from aiter.dist.parallel_state import get_tp_group

tp = get_tp_group()
if tp.world_size == 1:
_TBO_TP_UBATCH_COMMS = [None] * _NUM_UBATCHES
return None

# One independent communicator per ubatch. Construction runs a warmup
# all_reduce (a collective), so every rank must build the same number in the
# same order — guaranteed here because all ranks run TBO in lockstep and hit
# this on the same forward. Bound to the TP cpu_group (non-NCCL backend),
# exactly like the shared pynccl_comm.
comms = []
for _ in range(_NUM_UBATCHES):
comms.append(PyNcclCommunicator(group=tp.cpu_group, device=tp.device))
_TBO_TP_UBATCH_COMMS = comms
return _TBO_TP_UBATCH_COMMS[ubatch_id]
Copilot AI review requested due to automatic review settings July 23, 2026 08:52

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 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (3)

atom/utils/tbo/ubatching.py:318

  • _TBO_TP_UBATCH_COMMS is lazily initialized from the TBO worker threads, but there is no synchronization protecting the first-time construction. Because PyNcclCommunicator construction runs a warmup collective (per the comment below), two ubatch threads racing here can interleave communicator creation differently across ranks and hang. Add a module-level lock dedicated to guarding communicator initialization.
# Per-ubatch independent pynccl TP communicators for the pure-TP all_reduce
# overlap (see `tbo_all_reduce`).
_TBO_TP_UBATCH_COMMS: "list | None" = None

atom/utils/tbo/ubatching.py:328

  • tbo_get_ubatch_tp_comm() lazily builds PyNcclCommunicator instances without any mutual exclusion. Under TBO, both ubatch threads can hit this on the first forward, racing the constructor's warmup all_reduce and potentially deadlocking. Guard the initialization with the shared lock and double-check after acquiring it.
    global _TBO_TP_UBATCH_COMMS
    if _TBO_TP_UBATCH_COMMS is not None:
        return _TBO_TP_UBATCH_COMMS[ubatch_id]

    from aiter.dist.device_communicators.communicator_pynccl import (

atom/utils/tbo/ubatching.py:610

  • _TP_AR_ORDER_EVENT is created fresh each step and exported via tbo_get_tp_ar_order_event(), but it is not used anywhere (the new TBO all_reduce path in module_dispatch_ops doesn’t consult it). This dead state + docstring (“serializes the pure-TP all_reduce across ubatches”) is misleading and risks future callers relying on an ordering guarantee that doesn’t exist. Either wire the event into the overlap all_reduce path (e.g., record/wait between ubatches) or remove the event + getter entirely.
    # Fresh per-step event that serializes the pure-TP all_reduce across
    # ubatches (see `_TP_AR_ORDER_EVENT` note above). A new event each step
    # means the first ubatch's AR never waits on a prior step's recording.
    _TP_AR_ORDER_EVENT = torch.cuda.Event()

Comment on lines +1812 to 1816
# Route wo_b's TP all_reduce through the TBO-aware custom op only for the
# pure TP+TBO case (see _tbo_aware_tp_reduce). Non-TBO / TBO+DP keep the
# original all_reduce untouched.
self.tbo_aware = _tbo_aware_tp_reduce(tp_size)
self.n_local_heads = args.n_heads // tp_size
@ZhangLirong-amd
ZhangLirong-amd requested a review from yitingw1 July 23, 2026 09:03
Comment thread atom/model_ops/linear.py
if getattr(self, "tbo_aware", False):
y = torch.ops.aiter.tbo_all_reduce(y)
else:
y = get_tp_group().all_reduce(y, ca_fp8_quant=False)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

let’s have a ATOM AR layer to deal with, so we don't have to add these "if..else.." for every model/op with AR

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i would like something like from ato,.model_ops.communication_op import tensor_model_parallel_all_reduce

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

good idea

_TP_AR_ORDER_EVENT: "torch.cuda.Event | None" = None


def tbo_get_tp_ar_order_event() -> "torch.cuda.Event | None":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

never used?

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.

3 participants