Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d313f43
rmsnorm tests: reorganize variants, use helpers to reduce duplication…
cschenjunlin May 28, 2026
2b28750
add vector fast paths for layernorm variants
cschenjunlin Jun 4, 2026
d670743
refactor: extract scalar/vector load-store helpers to reduce duplicat…
cschenjunlin Jun 4, 2026
f6c474c
fix layernorm shared storage name error, use helpers to simplify tests
cschenjunlin Jun 12, 2026
d48c011
fix norm xscale dtype issue and extract test helpers
cschenjunlin Jun 16, 2026
cb7d5ed
fix python code style issue
cschenjunlin Jun 16, 2026
a3800e1
Merge branch 'main' into cjl/norm_optimization
cschenjunlin Jun 16, 2026
7dd9982
Merge branch 'main' into cjl/norm_optimization
coderfeli Jun 16, 2026
cfa9acd
Merge branch 'main' into cjl/norm_optimization
cschenjunlin Jun 17, 2026
947d00b
replace rsqrt API with fmath.rsqrt
cschenjunlin Jun 17, 2026
328e78a
Merge branch 'main' into cjl/norm_optimization
coderfeli Jun 17, 2026
c1b1211
remove M from builder
cschenjunlin Jun 22, 2026
24f5dd3
use fly.compile, and split large shape from default test configs
cschenjunlin Jun 22, 2026
084527f
fix python code style issue
cschenjunlin Jun 22, 2026
a4eafd8
Merge branch 'main' into cjl/norm_optimization
cschenjunlin Jun 22, 2026
0e740cf
fix python style issue
cschenjunlin Jun 22, 2026
0b2e535
make eps as build function parameter
cschenjunlin Jul 8, 2026
536b0ba
WIP rmsnorm: use vec8 loads for aligned bf16/f16 cases
cschenjunlin Jul 9, 2026
6a55fba
each block process multiple rows in aligned small-N vec8 path
cschenjunlin Jul 13, 2026
fa4ba4c
test_rmsnorm: add unaligned cases for tuning
cschenjunlin Jul 13, 2026
e6f2045
rmsnorm: support unaligned small-N vector path
cschenjunlin Jul 17, 2026
31da44a
Merge origin/main into cjl/norm_perf
cschenjunlin Jul 21, 2026
4172c75
norm: align layernorm and rmsnorm eps/vector coverage
cschenjunlin Jul 21, 2026
3e569d9
Merge branch 'main' into cjl/norm_perf
cschenjunlin Jul 21, 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
48 changes: 31 additions & 17 deletions kernels/norm/layernorm_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
LayerNorm(x) = (x - mean) / sqrt(var + eps) * gamma + beta

Two paths:
- Fast path (N == BLOCK_THREADS * VEC_WIDTH * 4): vectorised tiled copy,
register caching, pipelined gamma/beta loads.
- Fast path (N is a multiple of BLOCK_THREADS * VEC_WIDTH): vectorised
tiled copy, register caching, pipelined gamma/beta loads.
- Generic path (arbitrary N): scalar 2-pass implementation.
"""

Expand Down Expand Up @@ -123,6 +123,7 @@ def build_layernorm_module(N: int, dtype_str: str, store_stats: bool = False, ep

RED_SLOTS = max(1, (BLOCK_THREADS + WARP_SIZE - 1) // WARP_SIZE)
elem_bits = 32 if dtype_str == "f32" else 16
tile_cols = BLOCK_THREADS * VEC_WIDTH

SharedStorage = _make_reduction_storage(RED_SLOTS)

Expand Down Expand Up @@ -221,12 +222,12 @@ def compute_mean_rstd(sum_val, sumsq_val):
return mean, rstd

# ==================================================================
# Fast path: N == BLOCK_THREADS * VEC_WIDTH * 4
# Fast path: N is a multiple of BLOCK_THREADS * VEC_WIDTH
# Uses buffer_load / buffer_store for high-bandwidth vectorised
# memory access (same approach as preshuffle_gemm).
# ==================================================================
if const_expr(N == (BLOCK_THREADS * VEC_WIDTH * 4) and elem_bits <= 16):
num_tiles_py = 4
if const_expr(N >= tile_cols and N % tile_cols == 0 and elem_bits <= 16):
num_tiles_py = N // tile_cols
c_zero_f = fx.Float32(0.0)
thread_sum = c_zero_f
thread_sumsq = c_zero_f
Expand Down Expand Up @@ -566,6 +567,7 @@ def build_fused_add_layernorm_module(N: int, dtype_str: str):

RED_SLOTS = max(1, (BLOCK_THREADS + WARP_SIZE - 1) // WARP_SIZE)
elem_bits = 32 if dtype_str == "f32" else 16
tile_cols = BLOCK_THREADS * VEC_WIDTH

SharedStorage = _make_reduction_storage(RED_SLOTS)

Expand All @@ -583,7 +585,7 @@ def fused_add_layernorm_kernel(

elem_dtype = dtype_to_elem_type(dtype_str)
fm_fast = arith.FastMathFlags.fast
eps_c = EPS
eps_c = eps

lds = fx.SharedAllocator().allocate(SharedStorage).peek()
s_sum = lds.s_sum.view(fx.make_layout(RED_SLOTS, 1))
Expand Down Expand Up @@ -637,10 +639,10 @@ def compute_mean_rstd(sum_val, sumsq_val):
return mean, fmath.rsqrt(var + eps_c, fastmath=fm_fast)

# ==================================================================
# Fast path: N == BLOCK_THREADS * VEC_WIDTH * 4
# Fast path: N is a multiple of BLOCK_THREADS * VEC_WIDTH
# ==================================================================
if const_expr(N == (BLOCK_THREADS * VEC_WIDTH * 4) and elem_bits <= 16):
num_tiles_py = 4
if const_expr(N >= tile_cols and N % tile_cols == 0 and elem_bits <= 16):
num_tiles_py = N // tile_cols
c_zero_f = fx.Float32(0.0)
thread_sum = c_zero_f
thread_sumsq = c_zero_f
Expand Down Expand Up @@ -786,9 +788,11 @@ def _build_layernorm_quant_module(
*,
is_smooth: bool,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
RED_SLOTS = max(1, (BLOCK_THREADS + WARP_SIZE - 1) // WARP_SIZE)
elem_bits = 32 if dtype_str == "f32" else 16
tile_cols = BLOCK_THREADS * VEC_WIDTH
quant_dtype_max = _quant_dtype_max(quant_dtype_str)

SharedStorage = _make_reduction_storage(RED_SLOTS)
Expand All @@ -809,7 +813,7 @@ def layernorm_quant_kernel(
quant_dtype = _quant_dtype_to_elem_type(quant_dtype_str)

fm_fast = arith.FastMathFlags.fast
eps_c = EPS
eps_c = eps
n_float = float(N)
c_zero_f = fx.Float32(0.0)
c_one_f = fx.Float32(1.0)
Expand Down Expand Up @@ -894,10 +898,10 @@ def block_reduce_max(val):
return fx.memref_load(s_sum, 0)

# ==================================================================
# Fast path: N == BLOCK_THREADS * VEC_WIDTH * 4
# Fast path: N is a multiple of BLOCK_THREADS * VEC_WIDTH
# ==================================================================
if const_expr(N == (BLOCK_THREADS * VEC_WIDTH * 4) and elem_bits <= 16):
num_tiles_py = 4
if const_expr(N >= tile_cols and N % tile_cols == 0 and elem_bits <= 16):
num_tiles_py = N // tile_cols
quant_half_width = VEC_WIDTH // 2
abs_mask = full(VEC_WIDTH, fx.Uint32(0x7FFFFFFF), fx.Uint32)

Expand Down Expand Up @@ -1130,12 +1134,14 @@ def _build_fused_add_layernorm_quant_module(
*,
is_smooth: bool,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
arch = get_rocm_arch()
USE_HW_CVT_PK_BF16_F32 = (arch == "gfx950") or str(arch).startswith("gfx95")

RED_SLOTS = max(1, (BLOCK_THREADS + WARP_SIZE - 1) // WARP_SIZE)
elem_bits = 32 if dtype_str == "f32" else 16
tile_cols = BLOCK_THREADS * VEC_WIDTH
quant_dtype_max = _quant_dtype_max(quant_dtype_str)

SharedStorage = _make_reduction_storage(RED_SLOTS)
Expand All @@ -1158,7 +1164,7 @@ def fused_add_layernorm_quant_kernel(
quant_dtype = _quant_dtype_to_elem_type(quant_dtype_str)

fm_fast = arith.FastMathFlags.fast
eps_c = EPS
eps_c = eps
n_float = float(N)
c_zero_f = fx.Float32(0.0)
c_one_f = fx.Float32(1.0)
Expand Down Expand Up @@ -1243,10 +1249,10 @@ def block_reduce_max(val):
return fx.memref_load(s_sum, 0)

# ==================================================================
# Fast path: N == BLOCK_THREADS * VEC_WIDTH * 4
# Fast path: N is a multiple of BLOCK_THREADS * VEC_WIDTH
# ==================================================================
if const_expr(N == (BLOCK_THREADS * VEC_WIDTH * 4) and elem_bits <= 16):
num_tiles_py = 4
if const_expr(N >= tile_cols and N % tile_cols == 0 and elem_bits <= 16):
num_tiles_py = N // tile_cols
quant_half_width = VEC_WIDTH // 2
abs_mask = full(VEC_WIDTH, fx.Uint32(0x7FFFFFFF), fx.Uint32)

Expand Down Expand Up @@ -1506,51 +1512,59 @@ def build_layernorm_dynamicquant_module(
N: int,
dtype_str: str,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
return _build_layernorm_quant_module(
N,
dtype_str,
is_smooth=False,
quant_dtype_str=quant_dtype_str,
eps=eps,
)


def build_layernorm_smoothquant_module(
N: int,
dtype_str: str,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
return _build_layernorm_quant_module(
N,
dtype_str,
is_smooth=True,
quant_dtype_str=quant_dtype_str,
eps=eps,
)


def build_fused_add_layernorm_dynamicquant_module(
N: int,
dtype_str: str,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
return _build_fused_add_layernorm_quant_module(
N,
dtype_str,
is_smooth=False,
quant_dtype_str=quant_dtype_str,
eps=eps,
)


def build_fused_add_layernorm_smoothquant_module(
N: int,
dtype_str: str,
quant_dtype_str: str = "i8",
eps: float = EPS,
):
return _build_fused_add_layernorm_quant_module(
N,
dtype_str,
is_smooth=True,
quant_dtype_str=quant_dtype_str,
eps=eps,
)


Expand Down
Loading
Loading