|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import triton |
|
import triton.language as tl |
|
import torch |
|
import math |
|
import torch.nn.functional as F |
|
|
|
from torch.amp import custom_fwd, custom_bwd |
|
|
|
@triton.jit |
|
def _rmsnorm_fwd_kernel( |
|
X, |
|
Y, |
|
W, |
|
Rstd, |
|
stride_x_row, |
|
stride_y_row, |
|
N, |
|
eps, |
|
BLOCK_N: tl.constexpr, |
|
IS_EVEN_N: tl.constexpr |
|
): |
|
|
|
row = tl.program_id(0) |
|
X += row * stride_x_row |
|
Y += row * stride_y_row |
|
|
|
|
|
cols = tl.arange(0, BLOCK_N) |
|
x = tl.load(X + cols, mask=cols < N, other=0.0).to(tl.float32) |
|
|
|
xbar = tl.where(cols < N, x, 0.0) |
|
var = tl.sum(xbar * xbar, axis=0) / N |
|
rstd = 1 / tl.sqrt(var + eps) |
|
tl.store(Rstd + row, rstd) |
|
|
|
|
|
mask = cols < N |
|
if IS_EVEN_N: |
|
w = tl.load(W + cols).to(tl.float32) |
|
else: |
|
w = tl.load(W + cols, mask=mask).to(tl.float32) |
|
|
|
x_hat = x * rstd |
|
y = x_hat * w |
|
|
|
|
|
if IS_EVEN_N: |
|
tl.store(Y + cols, y) |
|
else: |
|
tl.store(Y + cols, y, mask=mask) |
|
|
|
@triton.jit |
|
def _rmsnorm_bwd_kernel( |
|
X, |
|
W, |
|
DY, |
|
DX, |
|
DW, |
|
Rstd, |
|
stride_x_row, |
|
stride_dy_row, |
|
stride_dx_row, |
|
M, |
|
N, |
|
eps, |
|
rows_per_program, |
|
BLOCK_N: tl.constexpr, |
|
IS_EVEN_N: tl.constexpr |
|
): |
|
|
|
row_block_id = tl.program_id(0) |
|
row_start = row_block_id * rows_per_program |
|
cols = tl.arange(0, BLOCK_N) |
|
mask = cols < N |
|
X += row_start * stride_x_row |
|
|
|
DY += row_start * stride_dy_row |
|
DX += row_start * stride_dx_row |
|
|
|
w = tl.load(W + cols, mask=mask).to(tl.float32) |
|
|
|
dw = tl.zeros((BLOCK_N,), dtype=tl.float32) |
|
|
|
row_end = min((row_block_id + 1) * rows_per_program, M) |
|
|
|
for row in range(row_start, row_end): |
|
|
|
if IS_EVEN_N: |
|
x = tl.load(X + cols).to(tl.float32) |
|
dy = tl.load(DY + cols).to(tl.float32) |
|
else: |
|
x = tl.load(X + cols, mask=mask, other=0).to(tl.float32) |
|
dy = tl.load(DY + cols, mask=mask, other=0).to(tl.float32) |
|
|
|
rstd = tl.load(Rstd + row) |
|
|
|
|
|
xhat = x * rstd |
|
if not IS_EVEN_N: |
|
xhat = tl.where(mask, xhat, 0.0) |
|
|
|
wdy = w * dy |
|
dw += dy * xhat |
|
|
|
c1 = tl.sum(xhat * wdy, axis=0) / N |
|
dx = (wdy - xhat * c1) * rstd |
|
|
|
tl.store(DX + cols, dx, mask=mask) |
|
|
|
X += stride_x_row |
|
|
|
DY += stride_dy_row |
|
DX += stride_dx_row |
|
|
|
tl.store(DW + row_block_id * N + cols, dw, mask=mask) |
|
|
|
|
|
|
|
torch.library.define("flasht5::rmsnorm_triton_fwd", "(Tensor X, Tensor W, float eps) -> (Tensor, Tensor)") |
|
|
|
@torch.library.impl("flasht5::rmsnorm_triton_fwd", "default") |
|
def rmsnorm_triton_fwd(X, weight, eps): |
|
|
|
M, N = X.shape |
|
|
|
assert X.stride(-1) == 1 |
|
|
|
assert weight.shape == (N,) |
|
assert weight.stride(-1) == 1 |
|
|
|
|
|
Y = torch.empty_like(X) |
|
assert Y.stride(-1) == 1 |
|
|
|
rstd = torch.empty((M,), dtype=torch.float32, device=X.device) |
|
|
|
|
|
MAX_FUSED_SIZE = 65536 // X.element_size() |
|
BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) |
|
assert N <= BLOCK_N |
|
|
|
|
|
with torch.cuda.device(X.device.index): |
|
_rmsnorm_fwd_kernel[(M,)]( |
|
X, |
|
Y, |
|
weight, |
|
rstd, |
|
X.stride(0), |
|
Y.stride(0), |
|
N, |
|
eps, |
|
BLOCK_N, |
|
(N % BLOCK_N == 0) |
|
) |
|
|
|
return Y, rstd |
|
|
|
|
|
@torch.library.register_fake("flasht5::rmsnorm_triton_fwd", rmsnorm_triton_fwd) |
|
def rmsnorm_triton_fwd_abstract(X, weight, eps): |
|
M, N = X.shape |
|
|
|
Y = torch.empty_like(X) |
|
rstd = torch.empty((M,), dtype=torch.float32, device=X.device) |
|
|
|
return Y, rstd |
|
|
|
torch.library.define("flasht5::rmsnorm_triton_bwd", "(Tensor dY, Tensor X, Tensor W, Tensor rstd, float eps) -> (Tensor, Tensor)") |
|
|
|
@torch.library.impl("flasht5::rmsnorm_triton_bwd", "default") |
|
def rmsnorm_triton_bwd( |
|
dy, |
|
x, |
|
weight, |
|
rstd, |
|
eps |
|
): |
|
M, N = x.shape |
|
assert x.stride(-1) == 1 |
|
assert dy.stride(-1) == 1 |
|
assert dy.shape == (M, N) |
|
|
|
assert weight.shape == (N,) |
|
assert weight.stride(-1) == 1 |
|
|
|
|
|
dx = torch.empty_like(x) |
|
|
|
|
|
MAX_FUSED_SIZE = 65536 // x.element_size() |
|
BLOCK_N = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) |
|
|
|
assert N <= BLOCK_N |
|
|
|
sm_count = torch.cuda.get_device_properties(x.device).multi_processor_count |
|
_dw = torch.empty((sm_count, N), dtype=torch.float32, device=weight.device) |
|
|
|
rows_per_program = math.ceil(M / sm_count) |
|
grid = (sm_count,) |
|
with torch.cuda.device(x.device.index): |
|
_rmsnorm_bwd_kernel[grid]( |
|
x, |
|
weight, |
|
dy, |
|
dx, |
|
_dw, |
|
rstd, |
|
x.stride(0), |
|
dy.stride(0), |
|
dx.stride(0), |
|
M, |
|
N, |
|
eps, |
|
rows_per_program, |
|
BLOCK_N, |
|
(N % BLOCK_N == 0) |
|
) |
|
dw = _dw.sum(0).to(weight.dtype) |
|
|
|
return dx, dw |
|
|
|
|
|
@torch.library.register_fake("flasht5::rmsnorm_triton_bwd", rmsnorm_triton_bwd) |
|
def rmsnorm_triton_bwd_abstract(dy, x, weight, rstd, eps): |
|
|
|
M, N = x.shape |
|
dx = torch.empty_like(x) |
|
dw = torch.empty((1, N), dtype=torch.float32, device=weight.device) |
|
|
|
|
|
return dx, dw |
|
|
|
|
|
class Fast_RMS_Layernorm(torch.autograd.Function): |
|
@staticmethod |
|
@custom_fwd(device_type="cuda") |
|
def forward(ctx, X, W, eps=1e-6): |
|
|
|
X_orig_shape = X.shape |
|
X = X.reshape(-1, X.shape[-1]) |
|
|
|
y, rstd, = torch.ops.flasht5.rmsnorm_triton_fwd(X, W, eps) |
|
|
|
y = y.reshape(X_orig_shape) |
|
|
|
|
|
ctx.save_for_backward(X, W, rstd) |
|
ctx.x_shape_og = X_orig_shape |
|
ctx.eps = eps |
|
|
|
return y |
|
|
|
@staticmethod |
|
def backward(ctx, dY): |
|
X, weight, rstd = ctx.saved_tensors |
|
dY = dY.reshape(-1, dY.shape[-1]) |
|
|
|
assert dY.shape == X.shape |
|
|
|
dx, dw = torch.ops.flasht5.rmsnorm_triton_bwd( |
|
dY, |
|
X, |
|
weight, |
|
rstd, |
|
ctx.eps |
|
) |
|
|
|
return dx.reshape(ctx.x_shape_og), dw, None |
|
|
|
def fast_rms_layernorm(X, W, eps): |
|
out = Fast_RMS_Layernorm.apply(X, W, eps) |
|
return out |
|
|