Files
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

731 lines
25 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import importlib
import logging
import pkgutil
from typing import List, Tuple
import torch
import torch.distributed as dist
from tokenspeed_kernel._triton import redirect_triton_to_tokenspeed_triton, tl, triton
# iris does plain ``import triton`` at module load time; route those bindings
# to the vendored ``tokenspeed_triton`` so iris and tokenspeed-kernel share a
# single triton distribution. See
# :func:`redirect_triton_to_tokenspeed_triton` for details.
with redirect_triton_to_tokenspeed_triton():
import iris # noqa: E402
# Pre-import every iris kernel module that does ``import triton`` at module
# load time (the CCL APIs above lazy-import them at call time, when the
# redirect is no longer active).
import iris.ccl.triton # noqa: E402
from iris.ccl import Config as _IrisConfig # noqa: E402
from iris.ccl.all_gather import all_gather as _iris_all_gather # noqa: E402
from iris.ccl.all_reduce import all_reduce as _iris_all_reduce # noqa: E402
from iris.ccl.reduce_scatter import ( # noqa: E402
reduce_scatter as _iris_reduce_scatter,
)
for _info in pkgutil.walk_packages(
iris.ccl.triton.__path__, prefix="iris.ccl.triton."
):
importlib.import_module(_info.name)
from tokenspeed_kernel.platform import current_platform # noqa: E402
logger = logging.getLogger(__file__)
_platform = current_platform()
__all__ = [
"IrisAllReduce",
"IrisRSAG",
"IrisAllReduceResidualRMSNorm",
"create_iris_state",
"iris_all_reduce",
"create_iris_rsag_state",
"create_iris_ar_rmsnorm_state",
"iris_allreduce_residual_rmsnorm",
"IRIS_AR_RMSNORM_STATES",
]
IRIS_AR_RMSNORM_STATES: dict = {}
def _get_available_gpu_memory(gpu_id: int, empty_cache: bool = True) -> float:
if torch.cuda.is_available():
with torch.cuda.device(gpu_id):
if empty_cache:
torch.cuda.empty_cache()
free_gpu_memory, _ = torch.cuda.mem_get_info()
return free_gpu_memory / (1 << 30)
return 0.0
_iris_ctx_singleton = None
def _get_or_create_iris_context(heap_size: int):
global _iris_ctx_singleton
if _iris_ctx_singleton is None:
_iris_ctx_singleton = iris.iris(heap_size=heap_size)
return _iris_ctx_singleton
class IrisRSAG(object):
def __init__(
self,
group: dist.ProcessGroup,
rank_in_group: int,
max_tokens: int,
hidden_size: int,
device: torch.device = None,
heap_size: int | None = None,
) -> None:
assert (
type(group) == dist.ProcessGroup
), f"Expected dist.ProcessGroup, got {type(group)}"
assert dist.is_initialized(), (
"torch.distributed must be initialized before constructing "
"IrisRSAG; call dist.init_process_group() first."
)
assert _platform.is_amd, (
"IrisRSAG currently targets AMD ROCm; " f"got non-AMD platform: {_platform}"
)
assert (
group == dist.group.WORLD or group.size() == dist.get_world_size()
), "iris.ccl all_gather/reduce_scatter do not accept a sub-group."
self.group = group
self.rank_in_group = rank_in_group
self.device = device or torch.device(f"cuda:{torch.cuda.current_device()}")
self.max_tokens = max_tokens
self.hidden_size = hidden_size
self.dtype = torch.bfloat16
self.world_size = group.size()
# Heap holds in/out flat buffers plus iris bookkeeping; over-provision
# similarly to ``IrisAllReduce`` to leave room for ring/spinlock flags.
if heap_size is None:
buf_bytes = max_tokens * hidden_size * self.dtype.itemsize
heap_size = max(1 << 28, 4 * buf_bytes + (16 << 20))
free_gpu_memory_begin = _get_available_gpu_memory(torch.cuda.current_device())
self._ctx = _get_or_create_iris_context(heap_size)
self._in_buff = self._ctx.empty((max_tokens, hidden_size), dtype=self.dtype)
self._out_buff = self._ctx.empty((max_tokens, hidden_size), dtype=self.dtype)
free_gpu_memory_after = _get_available_gpu_memory(torch.cuda.current_device())
logger.info(
"Iris RSAG symmetric-heap buffers allocated: %s GB",
free_gpu_memory_begin - free_gpu_memory_after,
)
assert self._ctx.get_num_ranks() == dist.get_world_size(), (
f"Iris world size {self._ctx.get_num_ranks()} "
f"!= torch world size {dist.get_world_size()}"
)
assert self.rank_in_group == self._ctx.get_rank(), (
f"rank mismatch: rank_in_group={self.rank_in_group}, "
f"iris rank={self._ctx.get_rank()}"
)
# -- token-distribution helpers (mirror sibling classes) ----------------
def get_token_dist(self, total_tokens_in_group: int) -> list:
token_list_in_group = []
for rank in range(self.world_size):
num_tokens_per_rank = total_tokens_in_group // self.world_size + (
1 if (rank < total_tokens_in_group % self.world_size) else 0
)
token_list_in_group.append(num_tokens_per_rank)
return token_list_in_group
def get_context(self, token_list_in_group: list) -> Tuple[int, int, int]:
total_num_tokens = sum(token_list_in_group)
assert (
total_num_tokens <= self.max_tokens
), f"The inner comm buffer is too small: {total_num_tokens=} is not <= {self.max_tokens=}"
local_num_tokens = token_list_in_group[self.rank_in_group]
local_token_offset = sum(token_list_in_group[: self.rank_in_group])
return total_num_tokens, local_num_tokens, local_token_offset
# -- internal helpers ---------------------------------------------------
def _assert_uniform(self, token_list_in_group: List[int]) -> int:
first = token_list_in_group[0]
assert all(t == first for t in token_list_in_group), (
"IrisRSAG requires uniform tokens per rank; got "
f"token_list_in_group={token_list_in_group}"
)
return first
@staticmethod
def _pick_block_n(hidden_size: int) -> int:
# Pick the largest power-of-two block that divides hidden_size, capped
# at 256. This keeps the iris kernel on its no-mask fast path and
# still produces enough tiles (world_size * hidden/block_n) to fill
# ``comm_sms`` SMs on MI300-class chips.
for cand in (256, 128, 64, 32, 16):
if hidden_size % cand == 0:
return cand
return hidden_size
def _make_config(self, local_num_tokens: int, hidden_size: int):
# ``swizzle_size=1`` keeps tile_id ordering row-major in M, which is
# required so that block-distribution (DISTRIBUTION=1) hands rank r
# exactly the K tiles spanning rows [r*local, (r+1)*local) in the
# reduce-scatter kernel. ``all_gather`` is rank-agnostic on tile order
# so the same config is fine.
return _IrisConfig(
block_size_m=local_num_tokens,
block_size_n=self._pick_block_n(hidden_size),
swizzle_size=1,
all_reduce_distribution=1,
)
# -- public collective ops ---------------------------------------------
def reduce_scatter(
self,
hidden_states: torch.Tensor,
tp_num_tokens: int = None,
token_list_in_group: List[int] = None,
safe=True,
) -> torch.Tensor:
assert (
tp_num_tokens is not None or token_list_in_group is not None
), "Either tp_num_tokens or token_list_in_group must be provided"
if token_list_in_group is None:
token_list_in_group = self.get_token_dist(tp_num_tokens)
assert (
hidden_states.dtype == self.dtype
), f"Only {self.dtype} is supported, got {hidden_states.dtype}"
local_num_tokens = self._assert_uniform(token_list_in_group)
total_num_tokens, _, local_token_offset = self.get_context(token_list_in_group)
assert (hidden_states.shape[0] == total_num_tokens) and (
hidden_states.shape[-1] == self.hidden_size
), (
f"Mismatched shape, {hidden_states.shape[0]=} != {total_num_tokens=} "
f"or {hidden_states.shape[-1]=} != {self.hidden_size=} "
f"{hidden_states.shape=}"
)
if local_num_tokens == 0:
return torch.empty(
(0, self.hidden_size),
dtype=hidden_states.dtype,
device=hidden_states.device,
)
in_view = self._in_buff[:total_num_tokens, : self.hidden_size]
out_view = self._out_buff[:total_num_tokens, : self.hidden_size]
in_view.copy_(hidden_states)
self._ctx.device_barrier()
config = self._make_config(local_num_tokens, self.hidden_size)
_iris_reduce_scatter(out_view, in_view, self._ctx, config=config)
output = out_view[local_token_offset : local_token_offset + local_num_tokens, :]
return output.clone() if safe else output
def all_gather(
self,
hidden_states: torch.Tensor,
tp_num_tokens: int = None,
token_list_in_group: List[int] = None,
safe=True,
) -> torch.Tensor:
assert (
tp_num_tokens is not None or token_list_in_group is not None
), "Either tp_num_tokens or token_list_in_group must be provided"
if token_list_in_group is None:
token_list_in_group = self.get_token_dist(tp_num_tokens)
assert (
hidden_states.dtype == self.dtype
), f"Only {self.dtype} is supported, got {hidden_states.dtype}"
local_num_tokens = self._assert_uniform(token_list_in_group)
total_num_tokens, _, _ = self.get_context(token_list_in_group)
hidden_size = hidden_states.shape[-1]
assert (hidden_states.shape[0] == local_num_tokens) and (
hidden_size <= self.hidden_size
), (
f"{hidden_states.shape=}|{local_num_tokens=}|{hidden_states.device=} "
"Mismatched shape"
)
if local_num_tokens == 0:
return torch.empty(
(0, hidden_size),
dtype=hidden_states.dtype,
device=hidden_states.device,
)
in_view = self._in_buff[:local_num_tokens, :hidden_size]
out_view = self._out_buff[:total_num_tokens, :hidden_size]
in_view.copy_(hidden_states)
self._ctx.device_barrier()
config = self._make_config(local_num_tokens, hidden_size)
_iris_all_gather(out_view, in_view, self._ctx, config=config)
return out_view.clone() if safe else out_view
class IrisAllReduce(object):
def __init__(
self,
group: dist.ProcessGroup,
rank_in_group: int,
max_numel: int,
dtype: torch.dtype = torch.bfloat16,
heap_size: int | None = None,
device: torch.device = None,
config=None,
) -> None:
assert (
type(group) == dist.ProcessGroup
), f"Expected dist.ProcessGroup, got {type(group)}"
assert dist.is_initialized(), (
"torch.distributed must be initialized before constructing "
"IrisAllReduce; call dist.init_process_group() first."
)
assert _platform.is_amd, (
"IrisAllReduce currently targets AMD ROCm; "
f"got non-AMD platform: {_platform}"
)
self.group = group
self.rank_in_group = rank_in_group
self.max_numel = max_numel
self.dtype = dtype
self.device = device or torch.device(f"cuda:{torch.cuda.current_device()}")
self._config = config or _IrisConfig(
block_size_m=32, block_size_n=64, all_reduce_distribution=1
)
# Heap holds two flat buffers of ``max_numel * itemsize`` plus iris
# bookkeeping; we leave generous headroom (~16 MiB) for internal
# workspaces such as ring/spinlock flags.
if heap_size is None:
buf_bytes = max_numel * dtype.itemsize
heap_size = max(1 << 28, 4 * buf_bytes + (16 << 20))
free_gpu_memory_begin = _get_available_gpu_memory(torch.cuda.current_device())
self._ctx = _get_or_create_iris_context(heap_size)
self._input_buf = self._ctx.zeros((max_numel,), dtype=dtype)
self._output_buf = self._ctx.zeros((max_numel,), dtype=dtype)
free_gpu_memory_after = _get_available_gpu_memory(torch.cuda.current_device())
logger.info(
"Iris all-reduce symmetric-heap buffers allocated: %s GB",
free_gpu_memory_begin - free_gpu_memory_after,
)
self.world_size = group.size()
def all_reduce(
self,
tensor: torch.Tensor,
op=None,
safe: bool = True,
async_op: bool = False,
) -> torch.Tensor:
assert tensor.dtype == self.dtype, (
f"Iris all-reduce dtype mismatch: tensor={tensor.dtype}, "
f"backend={self.dtype}"
)
numel = tensor.numel()
assert numel <= self.max_numel, (
f"tensor numel ({numel}) exceeds iris buffer capacity "
f"({self.max_numel})"
)
if tensor.dim() >= 2:
n_dim = tensor.shape[-1]
m_dim = numel // n_dim
else:
m_dim, n_dim = 1, numel
in_view = self._input_buf.narrow(0, 0, numel).view(m_dim, n_dim)
out_view = self._output_buf.narrow(0, 0, numel).view(m_dim, n_dim)
in_view.view(-1).copy_(tensor.view(-1))
self._ctx.device_barrier()
ar_group = None if self.group == dist.group.WORLD else self.group
_iris_all_reduce(
out_view,
in_view,
self._ctx,
op=op,
group=ar_group,
async_op=async_op,
config=self._config,
)
result = out_view.view(tensor.shape)
return result.clone() if safe else result
@triton.jit
def iris_allreduce_residual_rmsnorm_kernel(
input_sym_ptr, # base of symmetric (M, HIDDEN_SIZE) input buffer
residual_ptr, # local (M, HIDDEN_SIZE)
weight_ptr, # local (HIDDEN_SIZE,)
norm_out_ptr, # local (M, HIDDEN_SIZE)
residual_out_ptr, # local (M, HIDDEN_SIZE)
M,
heap_bases,
iris_rank: tl.constexpr,
world_size: tl.constexpr,
rank_start: tl.constexpr,
rank_stride: tl.constexpr,
HIDDEN_SIZE: tl.constexpr,
BLOCK_SIZE: tl.constexpr,
EPS: tl.constexpr,
):
row = tl.program_id(0)
if row >= M:
return
offsets = tl.arange(0, BLOCK_SIZE)
mask = offsets < HIDDEN_SIZE
row_offsets = row * HIDDEN_SIZE + offsets
in_row_ptr = input_sym_ptr + row_offsets
acc = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
for i in tl.static_range(0, world_size):
remote_rank = rank_start + i * rank_stride
acc += iris.load(
in_row_ptr,
iris_rank,
remote_rank,
heap_bases,
mask=mask,
other=0.0,
).to(tl.float32)
residual = tl.load(residual_ptr + row_offsets, mask=mask, other=0.0).to(tl.float32)
residual_out = acc + residual
res_out_dtype = residual_out_ptr.type.element_ty
tl.store(
residual_out_ptr + row_offsets,
residual_out.to(res_out_dtype),
mask=mask,
)
variance = tl.sum(residual_out * residual_out, axis=0) / HIDDEN_SIZE
scale = tl.rsqrt(variance + EPS)
weight = tl.load(weight_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
norm = residual_out * scale * weight
norm_dtype = norm_out_ptr.type.element_ty
tl.store(
norm_out_ptr + row_offsets,
norm.to(norm_dtype),
mask=mask,
)
@triton.jit
def iris_allreduce_residual_rmsnorm_kernel_persistent(
input_sym_ptr,
residual_ptr,
weight_ptr,
norm_out_ptr,
residual_out_ptr,
M,
heap_bases,
iris_rank: tl.constexpr,
world_size: tl.constexpr,
rank_start: tl.constexpr,
rank_stride: tl.constexpr,
HIDDEN_SIZE: tl.constexpr,
BLOCK_SIZE: tl.constexpr,
EPS: tl.constexpr,
):
pid = tl.program_id(0)
num_programs = tl.num_programs(0)
offsets = tl.arange(0, BLOCK_SIZE)
mask = offsets < HIDDEN_SIZE
weight = tl.load(weight_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
res_out_dtype = residual_out_ptr.type.element_ty
norm_dtype = norm_out_ptr.type.element_ty
for row in range(pid, M, num_programs):
row_offsets = row * HIDDEN_SIZE + offsets
in_row_ptr = input_sym_ptr + row_offsets
acc = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
for i in tl.static_range(0, world_size):
remote_rank = rank_start + i * rank_stride
acc += iris.load(
in_row_ptr,
iris_rank,
remote_rank,
heap_bases,
mask=mask,
other=0.0,
).to(tl.float32)
residual = tl.load(residual_ptr + row_offsets, mask=mask, other=0.0).to(
tl.float32
)
residual_out = acc + residual
tl.store(
residual_out_ptr + row_offsets,
residual_out.to(res_out_dtype),
mask=mask,
)
variance = tl.sum(residual_out * residual_out, axis=0) / HIDDEN_SIZE
scale = tl.rsqrt(variance + EPS)
norm = residual_out * scale * weight
tl.store(
norm_out_ptr + row_offsets,
norm.to(norm_dtype),
mask=mask,
)
class IrisAllReduceResidualRMSNorm(object):
def __init__(
self,
group: dist.ProcessGroup,
rank_in_group: int,
max_token_num: int,
hidden_dim: int,
dtype: torch.dtype = torch.bfloat16,
heap_size: int | None = None,
device: torch.device = None,
persistent: bool = False,
) -> None:
assert (
type(group) == dist.ProcessGroup
), f"Expected dist.ProcessGroup, got {type(group)}"
assert dist.is_initialized(), (
"torch.distributed must be initialized before constructing "
"IrisAllReduceResidualRMSNorm; call dist.init_process_group() first."
)
assert _platform.is_amd, (
"IrisAllReduceResidualRMSNorm currently targets AMD ROCm; "
f"got non-AMD platform: {_platform}"
)
self.group = group
self.rank_in_group = rank_in_group
self.world_size = group.size()
self.max_token_num = max_token_num
self.hidden_dim = hidden_dim
self.dtype = dtype
self.device = device or torch.device(f"cuda:{torch.cuda.current_device()}")
if heap_size is None:
buf_bytes = max_token_num * hidden_dim * dtype.itemsize
heap_size = max(1 << 28, 4 * buf_bytes + (16 << 20))
free_gpu_memory_begin = _get_available_gpu_memory(torch.cuda.current_device())
self._ctx = _get_or_create_iris_context(heap_size)
self._input_buf = self._ctx.zeros((max_token_num, hidden_dim), dtype=dtype)
free_gpu_memory_after = _get_available_gpu_memory(torch.cuda.current_device())
logger.info(
"Iris AR+RMSNorm symmetric-heap buffer allocated: %s GB",
free_gpu_memory_begin - free_gpu_memory_after,
)
self._rank_start = 0
self._rank_stride = 1
self._iris_rank = dist.get_rank()
self.persistent = persistent
self._num_programs = (
torch.cuda.get_device_properties(self.device).multi_processor_count
if persistent
else 0
)
def fused(
self,
input_tensor: torch.Tensor,
residual: torch.Tensor,
weight: torch.Tensor,
eps: float,
norm_out: torch.Tensor | None = None,
residual_out: torch.Tensor | None = None,
) -> Tuple[torch.Tensor, torch.Tensor]:
assert input_tensor.dtype == self.dtype, (
f"Iris AR+RMSNorm dtype mismatch: input={input_tensor.dtype}, "
f"backend={self.dtype}"
)
assert input_tensor.dim() == 2, (
f"input must be 2-D (num_tokens, hidden_dim), got "
f"shape={input_tensor.shape}"
)
assert (
input_tensor.shape == residual.shape
), f"residual shape {residual.shape} != input shape {input_tensor.shape}"
assert input_tensor.shape[1] == self.hidden_dim, (
f"hidden_dim mismatch: input={input_tensor.shape[1]} vs "
f"backend={self.hidden_dim}"
)
num_tokens = input_tensor.shape[0]
assert num_tokens <= self.max_token_num, (
f"num_tokens ({num_tokens}) exceeds max_token_num "
f"({self.max_token_num})"
)
assert weight.shape == (
self.hidden_dim,
), f"weight shape {weight.shape} != ({self.hidden_dim},)"
assert input_tensor.is_contiguous() and residual.is_contiguous()
in_view = self._input_buf[:num_tokens, :]
in_view.copy_(input_tensor)
if norm_out is None:
norm_out = torch.empty_like(input_tensor)
if residual_out is None:
residual_out = torch.empty_like(residual)
self._ctx.device_barrier()
heap_bases = self._ctx.get_heap_bases()
BLOCK_SIZE = triton.next_power_of_2(self.hidden_dim)
if self.persistent:
kernel = iris_allreduce_residual_rmsnorm_kernel_persistent
grid = (min(num_tokens, self._num_programs),)
else:
kernel = iris_allreduce_residual_rmsnorm_kernel
grid = (num_tokens,)
kernel[grid](
in_view,
residual,
weight,
norm_out,
residual_out,
num_tokens,
heap_bases,
iris_rank=self._iris_rank,
world_size=self.world_size,
rank_start=self._rank_start,
rank_stride=self._rank_stride,
HIDDEN_SIZE=self.hidden_dim,
BLOCK_SIZE=BLOCK_SIZE,
EPS=eps,
num_warps=8,
)
return norm_out, residual_out
def create_iris_state(
group: dist.ProcessGroup,
rank_in_group: int,
max_numel: int,
dtype: torch.dtype = torch.bfloat16,
heap_size: int | None = None,
device: torch.device = None,
) -> "IrisAllReduce":
return IrisAllReduce(
group=group,
rank_in_group=rank_in_group,
max_numel=max_numel,
dtype=dtype,
heap_size=heap_size,
device=device,
)
def iris_all_reduce(
state: "IrisAllReduce",
tensor: torch.Tensor,
op=None,
safe: bool = True,
async_op: bool = False,
) -> torch.Tensor:
return state.all_reduce(tensor, op=op, safe=safe, async_op=async_op)
def create_iris_rsag_state(
group: dist.ProcessGroup,
rank_in_group: int,
max_tokens: int,
hidden_size: int,
device: torch.device = None,
heap_size: int | None = None,
) -> "IrisRSAG":
return IrisRSAG(
group=group,
rank_in_group=rank_in_group,
max_tokens=max_tokens,
hidden_size=hidden_size,
device=device,
heap_size=heap_size,
)
def create_iris_ar_rmsnorm_state(
group: dist.ProcessGroup,
rank_in_group: int,
max_token_num: int,
hidden_dim: int,
dtype: torch.dtype = torch.bfloat16,
heap_size: int | None = None,
device: torch.device = None,
persistent: bool = False,
) -> "IrisAllReduceResidualRMSNorm":
return IrisAllReduceResidualRMSNorm(
group=group,
rank_in_group=rank_in_group,
max_token_num=max_token_num,
hidden_dim=hidden_dim,
dtype=dtype,
heap_size=heap_size,
device=device,
persistent=persistent,
)
def iris_allreduce_residual_rmsnorm(
state: "IrisAllReduceResidualRMSNorm",
input_tensor: torch.Tensor,
residual: torch.Tensor,
weight: torch.Tensor,
eps: float = 1e-6,
norm_out: torch.Tensor | None = None,
residual_out: torch.Tensor | None = None,
) -> Tuple[torch.Tensor, torch.Tensor]:
return state.fused(
input_tensor=input_tensor,
residual=residual,
weight=weight,
eps=eps,
norm_out=norm_out,
residual_out=residual_out,
)