Instructions to use replicate/aiter-kernels with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use replicate/aiter-kernels with Kernels:
# !pip install kernels from kernels import get_kernel # a version (or an explicit revision) is required; see the "Files and versions" tab for the available ones kernel = get_kernel("replicate/aiter-kernels", version=1) - Notebooks
- Google Colab
- Kaggle
File size: 7,708 Bytes
2976eec | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | # SPDX-License-Identifier: MIT
# Copyright (C) 2024-2025, Advanced Micro Devices, Inc. All rights reserved.
"""
Minimal Iris context wrapper for AITER communication operations.
This module provides a thin wrapper around the iris library context to
support reduce-scatter and all-gather operations. All core iris functions
(load, store, put, atomic_*, etc.) are provided by the iris library itself.
"""
import logging
import math
from typing import Optional
import torch
import iris
# If we got here, iris is available
IRIS_AVAILABLE = True
logger = logging.getLogger("aiter")
def calculate_heap_size(
M: int,
N: int,
dtype: "torch.dtype",
world_size: Optional[int] = None,
quant_mode: str = "none",
all_gather: bool = True,
overhead_factor: float = 1.2,
) -> int:
"""
Calculate required Iris heap size for communication operations.
This function estimates the total memory needed in the Iris symmetric heap
for reduce-scatter, RMSNorm, quantization, and all-gather operations.
Args:
M (int): Number of rows in input tensor
N (int): Number of columns in input tensor
dtype (torch.dtype): Data type of input tensor (e.g., torch.float32, torch.float16)
world_size (int, optional): Number of GPUs. If None, uses torch.distributed.get_world_size()
quant_mode (str): Quantization mode - "none", "fp8_per_token", or "fp4_per_token"
all_gather (bool): Whether all-gather is performed after quantization
overhead_factor (float): Safety margin multiplier (default: 1.2 for 20% overhead)
Returns:
int: Required heap size in bytes
Example:
>>> # For 2 GPUs, 4096x4096 float32 tensor with FP8 quant and all-gather
>>> heap_size = calculate_heap_size(4096, 4096, torch.float32,
... world_size=2, quant_mode="fp8_per_token")
>>> with IrisCommContext(heap_size=heap_size) as ctx:
... # Your operations here
... pass
"""
if world_size is None:
if not torch.distributed.is_initialized():
raise RuntimeError(
"torch.distributed not initialized and world_size not provided. "
"Either initialize torch.distributed or pass world_size explicitly."
)
world_size = torch.distributed.get_world_size()
# Calculate element size in bytes (fast path for common types)
if dtype in (torch.float32, torch.int32):
elem_bytes = 4
elif dtype in (torch.float16, torch.bfloat16, torch.int16):
elem_bytes = 2
elif dtype in (torch.float64, torch.int64):
elem_bytes = 8
elif dtype == torch.int8:
elem_bytes = 1
else:
# Fallback for uncommon types (e.g., float8, complex types, future dtypes)
elem_bytes = torch.empty(0, dtype=dtype).element_size()
M_shard = M // world_size
# Memory for input tensor (M x N)
mem_input = M * N * elem_bytes
# Memory for reduce-scatter output (M_shard x N)
mem_rs = M_shard * N * elem_bytes
# Memory for quantization output
mem_quant = 0
if quant_mode == "fp8_per_token":
mem_quant = M_shard * N # 1 byte per element
elif quant_mode == "fp4_per_token":
if N % 32 != 0:
raise ValueError("FP4 quantization requires N divisible by 32")
mem_quant = M_shard * (N // 2) # 0.5 bytes per element
# Memory for all-gather output
mem_gather = 0
if all_gather:
if quant_mode == "fp8_per_token":
mem_gather = M * N # 1 byte per element
elif quant_mode == "fp4_per_token":
mem_gather = M * (N // 2) # 0.5 bytes per element
else:
mem_gather = M * N * elem_bytes # Full precision
# Total with overhead
total_bytes = mem_input + mem_rs + mem_quant + mem_gather
total_with_overhead = int(math.ceil(total_bytes * overhead_factor))
logger.debug(
f"Heap size calculation: M={M}, N={N}, dtype={dtype}, world_size={world_size}, "
f"quant_mode={quant_mode}, all_gather={all_gather}\n"
f" Input: {mem_input:,} bytes\n"
f" RS buffer: {mem_rs:,} bytes\n"
f" Quant buffer: {mem_quant:,} bytes\n"
f" Gather buffer: {mem_gather:,} bytes\n"
f" Total (with {overhead_factor}x overhead): {total_with_overhead:,} bytes "
f"({total_with_overhead / (1024**3):.2f} GB)"
)
return total_with_overhead
class IrisCommContext:
"""
Minimal context wrapper for Iris-based communication operations.
This is a thin wrapper around iris.iris() that provides convenient access
to the iris context for use in reduce-scatter and all-gather operations.
Example:
>>> # Manual heap size
>>> with IrisCommContext(heap_size=2**30) as ctx:
>>> shard = ctx.iris_ctx.zeros((1024, 1024), dtype=torch.float32)
>>> full = all_gather(shard, ctx)
>>> # Automatic heap size calculation
>>> heap_size = calculate_heap_size(4096, 4096, torch.float32,
... world_size=2, quant_mode="fp8_per_token")
>>> with IrisCommContext(heap_size=heap_size) as ctx:
>>> # Guaranteed to have enough memory for your operations
>>> input_tensor = ctx.iris_ctx.empty((4096, 4096), dtype=torch.float32)
"""
def __init__(self, heap_size: int = 1 << 30):
"""
Initialize Iris communication context.
Args:
heap_size (int): Size of the symmetric heap in bytes. Default: 1GB
Use calculate_heap_size() to automatically determine the required size
based on your tensor dimensions and operations.
Example:
>>> # Option 1: Fixed size
>>> ctx = IrisCommContext(heap_size=2**30) # 1GB
>>> # Option 2: Auto-calculated size
>>> M, N = 4096, 4096
>>> heap_size = calculate_heap_size(M, N, torch.float32,
... world_size=2, quant_mode="fp8_per_token")
>>> ctx = IrisCommContext(heap_size=heap_size)
"""
if not IRIS_AVAILABLE:
raise RuntimeError("Iris library is not available. Please install iris.")
self.heap_size = heap_size
self.iris_ctx = None
self._initialized = False
def __enter__(self):
"""Initialize Iris context when entering context manager."""
if not self._initialized:
self.iris_ctx = iris.iris(heap_size=self.heap_size)
self._initialized = True
self.cur_rank = self.iris_ctx.cur_rank
self.num_ranks = self.iris_ctx.num_ranks
logger.info(
f"Iris context initialized: rank {self.cur_rank}/{self.num_ranks}, heap_size={self.heap_size}"
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Clean up when exiting context manager.
Used by Python's with statement (automatically called).
Example:
with IrisCommContext(...) as ctx:
pass
# __exit__() is called here automatically
"""
# Iris context cleanup is handled automatically
pass
@property
def is_initialized(self) -> bool:
"""Check if the Iris context has been initialized."""
return self._initialized
def get_heap_bases(self):
"""Get the heap bases tensor for use in Triton kernels."""
if not self.is_initialized:
raise RuntimeError("Iris context not initialized. Use as context manager.")
return self.iris_ctx.heap_bases
|