Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
a9b2746
Merge main into ROCm strict Attention
Aug 19, 2026
41bac5d
feat(attention): add strict ROCm Attention and RCCL path
Aug 19, 2026
21486d9
test(attention): make ROCm acceptance imports explicit
Aug 19, 2026
674d7ba
style-rocm-attention-lint
Aug 19, 2026
0934ca4
test-unify-cuda-rocm-attention
Aug 19, 2026
4c744b3
refactor(attention): consolidate ROCm implementation
Aug 19, 2026
22d3780
feat(attention): default strict runtime to decoupled ring schedule
Aug 19, 2026
aac0fb0
fix(attention): gate ROCm strict provenance
inaniloquentee Aug 20, 2026
d146d43
feat(attention): use strict vendor cores by platform
inaniloquentee Aug 20, 2026
4418d18
test(attention): accept strict ROCm RoPE provenance
inaniloquentee Aug 20, 2026
fe39824
test(attention): validate reported platform provenance
inaniloquentee Aug 20, 2026
6424b88
feat(gemm): align BF16 K reduction with TP tree
frank-2077 Aug 20, 2026
6c02774
feat(distributed): add ROCm transport-only deterministic collectives
frank-2077 Aug 20, 2026
6ad03ca
feat(ffn): add deterministic Qwen3 FFN assembly
frank-2077 Aug 20, 2026
f7dc7a9
test(ffn): cover staged BF16 FFN contract
frank-2077 Aug 20, 2026
49fa1c5
test(ffn): cover TP CP and SP topologies
frank-2077 Aug 20, 2026
f6ff094
test(attention): run strict references by logical row
inaniloquentee Aug 20, 2026
9ff0e6d
test(attention): report executed vendor backend
inaniloquentee Aug 20, 2026
1bc4177
fix(attention): narrow injected AITER callables
inaniloquentee Aug 20, 2026
7d04efe
test(ffn): allow ROCm RCCL teardown to complete
frank-2077 Aug 21, 2026
2f3f61d
perf: benchmark deterministic FFN on ROCm
frank-2077 Aug 21, 2026
9dc9a68
feat: add strict distributed Triton FFN for ROCm
frank-2077 Aug 23, 2026
809789c
docs: report MI300X Triton FFN performance
frank-2077 Aug 23, 2026
5222b68
refactor(ffn): make ROCm path Triton-native
frank-2077 Aug 23, 2026
5fe86f4
docs(ffn): publish Triton ROCm performance report
frank-2077 Aug 23, 2026
43a31ae
docs(bench): label shapes axes and accuracy references
frank-2077 Aug 23, 2026
6e61ef0
docs(bench): refresh shape and topology figures
frank-2077 Aug 23, 2026
08f47d9
bench(rocm): compare Triton FFN with official TP1
frank-2077 Aug 23, 2026
981c7ab
docs(bench): report TP1 FFN speed and exactness
frank-2077 Aug 23, 2026
e64abab
docs(bench): simplify ROCm result figures
frank-2077 Aug 23, 2026
13897a7
Merge branch 'test' into codex/ws2-rocm-strict-attention
zhangj1an Aug 24, 2026
bf36f05
docs(bench): add CUDA GPU and CPU comparison
frank-2077 Aug 24, 2026
3247e3e
feat(attention): route strict ROCm attention to Vime via contract dis…
zhangj1an Aug 24, 2026
3446042
feat(attention): bind TP/CP degree and add multi-rank strict validation
zhangj1an Aug 24, 2026
97d97ee
Merge branch 'test' into codex/ws2-rocm-strict-attention
zhangj1an Aug 24, 2026
7380bc8
perf(ffn): eliminate deterministic layout copies
frank-2077 Aug 26, 2026
6e0c220
perf(ffn): optimize deterministic gfx942 leaf
frank-2077 Aug 26, 2026
2162c19
Merge branch 'test' into codex/ws2-rocm-strict-attention
zhangj1an Aug 26, 2026
5cf23ec
feat(distributed): add deterministic ROCm collectives
Flink-ddd Aug 28, 2026
eb16e79
perf(distributed): remove ROCm collective hot-path allocations
Flink-ddd Aug 28, 2026
d36d70b
refactor(distributed): unify platform collectives module
Flink-ddd Aug 28, 2026
0e7b442
Merge remote-tracking branch 'origin/test' into codex/ws2-rocm-strict…
zhangj1an Aug 29, 2026
d5041a2
Merge remote-tracking branch 'origin/feat/rocm-deterministic-collecti…
zhangj1an Aug 29, 2026
e8e79e7
fix(build): repair ops.cpp preprocessor guards mangled by the merge
zhangj1an Aug 29, 2026
a8deab9
feat(kernels): add bitwise Triton deterministic Attention core
zhangj1an Aug 29, 2026
cc76c48
bench(attention): add WS2 strict ROCm Attention report on MI300X
zhangj1an Aug 29, 2026
0f8a4f9
bench(attention): measure the per-KV-group schedule and correct the ~…
zhangj1an Aug 29, 2026
e24b594
bench(attention): drop the stale pre-per-KV-group MI300X performance …
zhangj1an Aug 29, 2026
f4602c0
bench(attention): support CPU and CUDA hosts, add the exactness heatmap
zhangj1an Aug 29, 2026
fd2b329
bench(attention): migrate result keys to the reference-native rename
zhangj1an Aug 29, 2026
15e123f
bench(attention): add the host run and fold the schedule cost into th…
zhangj1an Aug 29, 2026
279ee20
bench(attention): skip a cell instead of waiting on it forever
zhangj1an Aug 29, 2026
f0513d2
chore: trigger dco app
Flink-ddd Aug 29, 2026
1a2e4fc
bench(attention): add the CPU host sweep and a hard per-cell time budget
zhangj1an Aug 29, 2026
a55233f
perf(distributed): optimize deterministic ROCm collectives
maxiaosong1124 Aug 29, 2026
caef501
perf(distributed): adopt optimized ROCm collectives
frank-2077 Aug 29, 2026
19e0ee6
bench(rocm): update distributed FFN results
frank-2077 Aug 29, 2026
0831aa8
Merge pull request #361 from RL-Align/test
Flink-ddd Aug 30, 2026
2440a1b
Merge branch 'main' into codex/ws2-rocm-strict-attention
zhangj1an Aug 30, 2026
b5c948a
feat(attention): add the strict ROCm runtime and open CP on the dispa…
zhangj1an Aug 30, 2026
c57a8ae
Optimize deterministic rollout tensor-parallel all-reduce
inaniloquentee Aug 30, 2026
9d5732b
Optimize deterministic rollout tensor-parallel all-reduce (#365)
inaniloquentee Aug 30, 2026
99eebae
refactor(attention): resolve the ROCm CP transport through the shared…
zhangj1an Aug 30, 2026
553e9a5
merge: resolve test conflicts for ROCm collectives
Flink-ddd Aug 30, 2026
285dda1
style(distributed): satisfy collective formatting
Flink-ddd Aug 30, 2026
66c6276
Merge latest PR #356 into PR #357
maxiaosong1124 Aug 30, 2026
52a41fb
Merge PR #357 (perf/rocm-deterministic-collectives-ipc) into codex/ws…
zhangj1an Aug 30, 2026
ca5313f
feat(attention): validate the AITER schema the strict ROCm core calls…
zhangj1an Aug 30, 2026
4f8704b
feat(attention): add the strict ROCm decode path over a paged KV cache
zhangj1an Aug 30, 2026
8e32313
Enable exact-batch CUDA graphs for strict rollout
inaniloquentee Aug 30, 2026
9b95e34
Precompile strict FA4 training kernels
inaniloquentee Aug 31, 2026
0aa1d63
Add strict FA4 training precompile
inaniloquentee Aug 31, 2026
afccecc
Optimize small deterministic all-reduce path
inaniloquentee Aug 31, 2026
01b4ae4
Merge pull request #367 from RL-Align/codex/deterministic-full-decode…
Flink-ddd Aug 31, 2026
afede7a
perf(ffn): tune gfx942 TP2 shard leaves
frank-2077 Aug 31, 2026
7f653b6
Merge branch 'codex/ws2-rocm-strict-attention' into feat/rocm-strict-ffn
frank-2077 Aug 31, 2026
b068ce2
Merge remote-tracking branch 'origin/main' into pr-325
frank-2077 Aug 31, 2026
b2b4786
Merge remote-tracking branch 'fork/feat/rocm-strict-ffn' into pr-325
frank-2077 Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
469 changes: 469 additions & 0 deletions benchmarks/benchmark_rocm_attention.py

Large diffs are not rendered by default.

290 changes: 290 additions & 0 deletions benchmarks/benchmark_rocm_collectives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

"""Benchmark deterministic ROCm collectives against native RCCL.

Example:

torchrun --standalone --nproc-per-node=8 \
benchmarks/benchmark_rocm_collectives.py \
--size-bytes 4096 65536 1048576 16777216 \
--output benchmarks/results/rocm_collectives_mi300x.json

The native RCCL rows are performance references only. They are not used as a
bitwise correctness oracle because their floating-point reduction order is not
part of the strict deterministic contract.
"""

from __future__ import annotations

import argparse
import json
import os
import statistics
import time
from pathlib import Path
from typing import Callable, Sequence

import torch
import torch.distributed as dist

from rl_engine.distributed import RCCLDeterministicCollective

_DTYPES = {
"bf16": torch.bfloat16,
"fp16": torch.float16,
"fp32": torch.float32,
}
_OPERATIONS = ("all_reduce", "all_gather", "reduce_scatter")


def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--size-bytes",
type=int,
nargs="+",
default=[4 * 1024, 64 * 1024, 1024 * 1024, 16 * 1024 * 1024],
)
parser.add_argument("--dtype", choices=tuple(_DTYPES), default="bf16")
parser.add_argument("--operations", nargs="+", choices=_OPERATIONS, default=_OPERATIONS)
parser.add_argument("--warmup", type=int, default=10)
parser.add_argument("--iterations", type=int, default=50)
parser.add_argument("--samples", type=int, default=5)
parser.add_argument("--output", type=Path)
return parser.parse_args(argv)


def _validate_args(args: argparse.Namespace) -> None:
if any(size <= 0 for size in args.size_bytes):
raise ValueError("every --size-bytes value must be positive")
if args.warmup < 0:
raise ValueError("--warmup must be non-negative")
if args.iterations <= 0 or args.samples <= 0:
raise ValueError("--iterations and --samples must be positive")


def _timed_sample(operation: Callable[[], None], *, warmup: int, iterations: int) -> float:
for _ in range(warmup):
operation()
torch.cuda.synchronize()
dist.barrier()
start = time.perf_counter()
for _ in range(iterations):
operation()
torch.cuda.synchronize()
elapsed = (time.perf_counter() - start) / iterations

# Report the slowest rank, which is the end-to-end collective latency.
elapsed_tensor = torch.tensor([elapsed], dtype=torch.float64, device="cuda")
dist.all_reduce(elapsed_tensor, op=dist.ReduceOp.MAX)
return float(elapsed_tensor.item())


def _benchmark(
operation: Callable[[], None],
*,
warmup: int,
iterations: int,
samples: int,
) -> dict[str, object]:
timings = [
_timed_sample(operation, warmup=warmup if index == 0 else 0, iterations=iterations)
for index in range(samples)
]
median = statistics.median(timings)
return {
"median_us": median * 1.0e6,
"min_us": min(timings) * 1.0e6,
"max_us": max(timings) * 1.0e6,
"samples_us": [value * 1.0e6 for value in timings],
}


def _make_inputs(
*,
size_bytes: int,
dtype: torch.dtype,
world_size: int,
rank: int,
device: torch.device,
) -> tuple[torch.Tensor, int]:
element_size = torch.empty((), dtype=dtype).element_size()
elements = max(world_size, size_bytes // element_size)
elements -= elements % world_size
generator = torch.Generator(device="cpu").manual_seed(942 + rank)
tensor = torch.randn(elements, generator=generator, dtype=torch.float32).to(
device=device,
dtype=dtype,
)
return tensor.contiguous(), elements * element_size


def _operation_pair(
name: str,
input_tensor: torch.Tensor,
collective: RCCLDeterministicCollective,
world_size: int,
) -> tuple[Callable[[], None], Callable[[], None], torch.Tensor, torch.Tensor]:
if name == "all_reduce":
deterministic_out = torch.empty_like(input_tensor)
native_out = torch.empty_like(input_tensor)

def deterministic() -> None:
collective.all_reduce(input_tensor, out=deterministic_out)

def native() -> None:
native_out.copy_(input_tensor)
dist.all_reduce(native_out)

elif name == "all_gather":
output_shape = (input_tensor.numel() * world_size,)
deterministic_out = torch.empty(output_shape, dtype=input_tensor.dtype, device="cuda")
native_out = torch.empty_like(deterministic_out)

def deterministic() -> None:
collective.all_gather(input_tensor, out=deterministic_out)

def native() -> None:
dist.all_gather_into_tensor(native_out, input_tensor)

elif name == "reduce_scatter":
output_shape = (input_tensor.numel() // world_size,)
deterministic_out = torch.empty(output_shape, dtype=input_tensor.dtype, device="cuda")
native_out = torch.empty_like(deterministic_out)

def deterministic() -> None:
collective.reduce_scatter(input_tensor, out=deterministic_out)

def native() -> None:
dist.reduce_scatter_tensor(native_out, input_tensor)

else: # pragma: no cover - argparse constrains this value
raise ValueError(f"unsupported operation: {name}")

return deterministic, native, deterministic_out, native_out


def run(args: argparse.Namespace) -> dict[str, object] | None:
_validate_args(args)
if torch.version.hip is None or not torch.cuda.is_available():
raise RuntimeError("the ROCm collective benchmark requires an available AMD GPU")

local_rank = int(os.environ.get("LOCAL_RANK", "0"))
torch.cuda.set_device(local_rank)
dist.init_process_group("nccl", init_method="env://")
rank = dist.get_rank()
world_size = dist.get_world_size()
if world_size not in (2, 4, 8):
raise RuntimeError(f"the benchmark requires 2, 4, or 8 ranks, got {world_size}")
device = torch.device("cuda", local_rank)
dtype = _DTYPES[args.dtype]
max_size_bytes = max(args.size_bytes) + dtype.itemsize * world_size

rows: list[dict[str, object]] = []
try:
with RCCLDeterministicCollective(
device=device,
max_size_bytes=max_size_bytes,
) as collective:
for requested_size in args.size_bytes:
input_tensor, actual_size = _make_inputs(
size_bytes=requested_size,
dtype=dtype,
world_size=world_size,
rank=rank,
device=device,
)
for name in args.operations:
deterministic, native, deterministic_out, native_out = _operation_pair(
name,
input_tensor,
collective,
world_size,
)
deterministic()
deterministic_repeat = deterministic_out.clone()
deterministic()
repeat_bitwise = bool(torch.equal(deterministic_out, deterministic_repeat))
native()
max_abs_vs_native = float(
(deterministic_out.float() - native_out.float()).abs().max().item()
)

torch.cuda.reset_peak_memory_stats(device)
deterministic_timing = _benchmark(
deterministic,
warmup=args.warmup,
iterations=args.iterations,
samples=args.samples,
)
deterministic_peak = int(torch.cuda.max_memory_allocated(device))
torch.cuda.reset_peak_memory_stats(device)
native_timing = _benchmark(
native,
warmup=args.warmup,
iterations=args.iterations,
samples=args.samples,
)
native_peak = int(torch.cuda.max_memory_allocated(device))
deterministic_us = float(deterministic_timing["median_us"])
native_us = float(native_timing["median_us"])
rows.append(
{
"operation": name,
"requested_size_bytes": requested_size,
"actual_input_bytes": actual_size,
"dtype": args.dtype,
"deterministic": deterministic_timing,
"native_rccl": native_timing,
"latency_ratio_vs_native": deterministic_us / native_us,
"deterministic_input_gbps": actual_size / (deterministic_us * 1.0e3),
"native_input_gbps": actual_size / (native_us * 1.0e3),
"repeat_bitwise": repeat_bitwise,
"max_abs_vs_native": max_abs_vs_native,
"deterministic_workspace_bytes": collective.workspace_size_bytes,
"deterministic_peak_allocated_bytes": deterministic_peak,
"native_peak_allocated_bytes": native_peak,
}
)

reports: list[list[dict[str, object]] | None] = [None] * world_size
dist.all_gather_object(reports, rows)
if rank != 0:
return None
payload = {
"schema_version": "rlkernel.rocm_collective_benchmark.v1",
"world_size": world_size,
"device": torch.cuda.get_device_name(device),
"hip_version": torch.version.hip,
"collective_backend": RCCLDeterministicCollective.backend_id,
"reduction_order": RCCLDeterministicCollective.reduction_order,
"supports_compute_communication_fusion": False,
"warmup": args.warmup,
"iterations": args.iterations,
"samples": args.samples,
"rows": rows,
"all_rank_repeat_bitwise": all(
bool(row["repeat_bitwise"])
for rank_rows in reports
if rank_rows is not None
for row in rank_rows
),
}
if args.output is not None:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(payload, indent=2), encoding="utf-8")
return payload
finally:
dist.destroy_process_group()


def main(argv: Sequence[str] | None = None) -> int:
payload = run(parse_args(argv))
if payload is not None:
print(json.dumps(payload, indent=2))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading