Download model/triton_kernels.py from bobbydeveloper2014/forge-500m-chat: direct link, hf CLI and curl.
- Browser
- Download file 18 kB
-
https://huggingface.co/bobbydeveloper2014/forge-500m-chat/resolve/main/model/triton_kernels.py
- Command line
-
hf download hf://bobbydeveloper2014/forge-500m-chat/model/triton_kernels.py
-
curl -L -o triton_kernels.py https://huggingface.co/bobbydeveloper2014/forge-500m-chat/resolve/main/model/triton_kernels.py
18 kB
| """ | |
| FORGE-3B Triton GPU Kernels | |
| Hand-written Triton kernels for maximum GPU throughput: | |
| - Fused RMSNorm / DGN (Differential Group Normalization) | |
| - Fused SwiGLU activation | |
| - Complex SSM scan kernel | |
| - Fused MoE dispatch + expert compute + gather | |
| - Fused RoPE injection | |
| All kernels target H100/A100 with BF16 inputs. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| import torch | |
| import torch.nn.functional as F | |
| import os | |
| try: | |
| import triton | |
| import triton.language as tl | |
| TRITON_AVAILABLE = os.environ.get("FORGE_NO_TRITON") != "1" | |
| except ImportError: | |
| TRITON_AVAILABLE = False | |
| import warnings | |
| warnings.warn("Triton not available β falling back to PyTorch ops. " | |
| "Install triton for 2-4Γ kernel speedup.") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # FUSED RMSNorm KERNEL | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if TRITON_AVAILABLE: | |
| def _rms_norm_fwd_kernel( | |
| X, # input ptr (N, D) | |
| W, # weight ptr (D,) | |
| B, # bias ptr (D,) or None | |
| Y, # output ptr (N, D) | |
| Rstd, # 1/rms ptr (N,) β saved for backward | |
| stride_x, # stride along N dim | |
| N, # number of rows | |
| D, # row dimension | |
| eps, | |
| HAS_BIAS: tl.constexpr, | |
| BLOCK_D: tl.constexpr, | |
| ): | |
| row = tl.program_id(0) | |
| X_ptr = X + row * stride_x | |
| Y_ptr = Y + row * stride_x | |
| # Compute mean square | |
| ms = tl.zeros([BLOCK_D], dtype=tl.float32) | |
| for off in range(0, D, BLOCK_D): | |
| cols = off + tl.arange(0, BLOCK_D) | |
| mask = cols < D | |
| x = tl.load(X_ptr + cols, mask=mask, other=0.0).to(tl.float32) | |
| ms += x * x | |
| ms = tl.sum(ms, axis=0) / D | |
| rstd = 1.0 / tl.sqrt(ms + eps) | |
| tl.store(Rstd + row, rstd) | |
| # Normalize and scale | |
| for off in range(0, D, BLOCK_D): | |
| cols = off + tl.arange(0, BLOCK_D) | |
| mask = cols < D | |
| x = tl.load(X_ptr + cols, mask=mask, other=0.0).to(tl.float32) | |
| w = tl.load(W + cols, mask=mask, other=1.0).to(tl.float32) | |
| y = x * rstd * w | |
| if HAS_BIAS: | |
| b = tl.load(B + cols, mask=mask, other=0.0).to(tl.float32) | |
| y = y + b | |
| tl.store(Y_ptr + cols, y.to(tl.bfloat16), mask=mask) | |
| def _dgn_fwd_kernel( | |
| X, # (N, D) input | |
| W, # (G, Dg) per-group weight | |
| B, # (G, Dg) per-group bias | |
| Y, # (N, D) output | |
| Rstd, # (N, G) per-group rstd | |
| stride_x, | |
| N, D, G, # n_rows, d_model, n_groups | |
| Dg, # group size = D / G | |
| eps, | |
| BLOCK_Dg: tl.constexpr, | |
| ): | |
| """Differential Group Normalization: separate RMS per feature group.""" | |
| row = tl.program_id(0) | |
| grp = tl.program_id(1) | |
| base_x = row * stride_x + grp * Dg | |
| # Compute group RMS | |
| ms = tl.zeros([BLOCK_Dg], dtype=tl.float32) | |
| for off in range(0, Dg, BLOCK_Dg): | |
| cols = off + tl.arange(0, BLOCK_Dg) | |
| mask = cols < Dg | |
| x = tl.load(X + base_x + cols, mask=mask, other=0.0).to(tl.float32) | |
| ms += x * x | |
| ms = tl.sum(ms, axis=0) / Dg | |
| rstd = 1.0 / tl.sqrt(ms + eps) | |
| tl.store(Rstd + row * G + grp, rstd) | |
| # Normalize, scale, bias | |
| for off in range(0, Dg, BLOCK_Dg): | |
| cols = off + tl.arange(0, BLOCK_Dg) | |
| mask = cols < Dg | |
| x = tl.load(X + base_x + cols, mask=mask, other=0.0).to(tl.float32) | |
| w = tl.load(W + grp * Dg + cols, mask=mask, other=1.0).to(tl.float32) | |
| b = tl.load(B + grp * Dg + cols, mask=mask, other=0.0).to(tl.float32) | |
| y = x * rstd * w + b | |
| tl.store(Y + base_x + cols, y.to(tl.bfloat16), mask=mask) | |
| def _swiglu_fwd_kernel( | |
| Gate, # (N, D) gate branch | |
| Up, # (N, D) up branch | |
| Out, # (N, D) output = silu(Gate) * Up | |
| N, D, | |
| BLOCK_D: tl.constexpr, | |
| ): | |
| """Fused SwiGLU: Out = SiLU(Gate) * Up""" | |
| row = tl.program_id(0) | |
| for off in range(0, D, BLOCK_D): | |
| cols = off + tl.arange(0, BLOCK_D) | |
| mask = cols < D | |
| g = tl.load(Gate + row * D + cols, mask=mask).to(tl.float32) | |
| u = tl.load(Up + row * D + cols, mask=mask).to(tl.float32) | |
| # SiLU(x) = x * sigmoid(x) | |
| silu_g = g * tl.sigmoid(g) | |
| out = silu_g * u | |
| tl.store(Out + row * D + cols, out.to(tl.bfloat16), mask=mask) | |
| def _rope_fwd_kernel( | |
| Q, # (B, H, T, D) queries | |
| K, # (B, H_kv, T, D) keys | |
| Cos, # (T, D/2) cosine | |
| Sin, # (T, D/2) sine | |
| Q_out, | |
| K_out, | |
| B, H_q, H_kv, T, D, | |
| BLOCK_T: tl.constexpr, | |
| BLOCK_D: tl.constexpr, | |
| ): | |
| """Fused RoPE for Q and K.""" | |
| b_idx = tl.program_id(0) | |
| h_idx = tl.program_id(1) | |
| is_kv_head = h_idx >= H_q | |
| for t in range(0, T, BLOCK_T): | |
| t_off = t + tl.arange(0, BLOCK_T) | |
| t_mask = t_off < T | |
| for d in range(0, D // 2, BLOCK_D): | |
| d_off = d + tl.arange(0, BLOCK_D) | |
| d_mask = d_off < (D // 2) | |
| # Load cos/sin | |
| cos = tl.load(Cos + t_off[:, None] * (D // 2) + d_off[None, :], | |
| mask=t_mask[:, None] & d_mask[None, :], other=1.0).to(tl.float32) | |
| sin = tl.load(Sin + t_off[:, None] * (D // 2) + d_off[None, :], | |
| mask=t_mask[:, None] & d_mask[None, :], other=0.0).to(tl.float32) | |
| if not is_kv_head: | |
| ptr_base = Q + b_idx * H_q * T * D + h_idx * T * D | |
| out_base = Q_out + b_idx * H_q * T * D + h_idx * T * D | |
| # x1 = x[..., :D/2], x2 = x[..., D/2:] | |
| x1 = tl.load(ptr_base + t_off[:, None] * D + d_off[None, :], | |
| mask=t_mask[:, None] & d_mask[None, :]).to(tl.float32) | |
| x2 = tl.load(ptr_base + t_off[:, None] * D + (D // 2) + d_off[None, :], | |
| mask=t_mask[:, None] & d_mask[None, :]).to(tl.float32) | |
| y1 = x1 * cos - x2 * sin | |
| y2 = x1 * sin + x2 * cos | |
| tl.store(out_base + t_off[:, None] * D + d_off[None, :], | |
| y1.to(tl.bfloat16), mask=t_mask[:, None] & d_mask[None, :]) | |
| tl.store(out_base + t_off[:, None] * D + (D // 2) + d_off[None, :], | |
| y2.to(tl.bfloat16), mask=t_mask[:, None] & d_mask[None, :]) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # PYTHON WRAPPERS AROUND TRITON KERNELS | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class FusedDGN(torch.autograd.Function): | |
| """ | |
| Fused Differential Group Normalization using Triton kernel. | |
| Fallback to PyTorch if Triton unavailable. | |
| """ | |
| def forward(ctx, x: torch.Tensor, weight: torch.Tensor, | |
| bias: torch.Tensor, n_groups: int, eps: float): | |
| """ | |
| x: (batch, seq, d_model) or (N, d_model) | |
| weight, bias: (d_model,) | |
| """ | |
| orig_shape = x.shape | |
| ctx.orig_shape = orig_shape | |
| x_2d = x.reshape(-1, x.shape[-1]).contiguous() | |
| N, D = x_2d.shape | |
| G = n_groups | |
| Dg = D // G | |
| if not TRITON_AVAILABLE or not x.is_cuda: | |
| # Pure PyTorch fallback | |
| out = _dgn_pytorch(x_2d, weight, bias, G, Dg, eps) | |
| ctx.save_for_backward(x_2d, weight, bias, | |
| _compute_rstd_pytorch(x_2d, G, Dg, eps)) | |
| ctx.n_groups = G | |
| ctx.Dg = Dg | |
| ctx.eps = eps | |
| return out.reshape(orig_shape) | |
| y = torch.empty_like(x_2d) | |
| rstd = torch.empty(N, G, device=x.device, dtype=torch.float32) | |
| BLOCK_Dg = min(triton.next_power_of_2(Dg), 128) | |
| grid = (N, G) | |
| _dgn_fwd_kernel[grid]( | |
| x_2d, weight.reshape(G, Dg), bias.reshape(G, Dg), | |
| y, rstd, | |
| x_2d.stride(0), N, D, G, Dg, eps, | |
| BLOCK_Dg=BLOCK_Dg, | |
| ) | |
| ctx.save_for_backward(x_2d, weight, bias, rstd) | |
| ctx.n_groups = G | |
| ctx.Dg = Dg | |
| ctx.eps = eps | |
| return y.reshape(orig_shape) | |
| def backward(ctx, grad_output): | |
| x, weight, bias, rstd = ctx.saved_tensors | |
| G, Dg, eps = ctx.n_groups, ctx.Dg, ctx.eps | |
| # Backward via PyTorch autograd (exact, Triton bwd TBD) | |
| with torch.enable_grad(): | |
| x_r = x.detach().requires_grad_(True) | |
| w_r = weight.detach().requires_grad_(True) | |
| b_r = bias.detach().requires_grad_(True) | |
| y = _dgn_pytorch(x_r, w_r, b_r, G, Dg, eps) | |
| y.backward(grad_output.reshape_as(y)) | |
| return x_r.grad.reshape(ctx.orig_shape), w_r.grad, b_r.grad, None, None | |
| def _dgn_pytorch(x: torch.Tensor, weight: torch.Tensor, bias: torch.Tensor, | |
| G: int, Dg: int, eps: float) -> torch.Tensor: | |
| """PyTorch reference for DGN β used in backward and as fallback.""" | |
| N, D = x.shape | |
| x_g = x.reshape(N, G, Dg) | |
| rms = x_g.float().pow(2).mean(dim=-1, keepdim=True).add(eps).sqrt() | |
| x_norm = (x_g / rms).reshape(N, D) | |
| return x_norm.to(x.dtype) * weight + bias | |
| def _compute_rstd_pytorch(x: torch.Tensor, G: int, Dg: int, eps: float) -> torch.Tensor: | |
| N, D = x.shape | |
| x_g = x.reshape(N, G, Dg).float() | |
| rms = x_g.pow(2).mean(dim=-1).add(eps).sqrt() | |
| return 1.0 / rms | |
| def fused_dgn(x: torch.Tensor, weight: torch.Tensor, bias: torch.Tensor, | |
| n_groups: int = 16, eps: float = 1e-6) -> torch.Tensor: | |
| """Entry point for fused DGN β uses Triton if available, else PyTorch.""" | |
| return FusedDGN.apply(x, weight, bias, n_groups, eps) | |
| def fused_swiglu(gate: torch.Tensor, up: torch.Tensor) -> torch.Tensor: | |
| """Fused SwiGLU: output = SiLU(gate) * up""" | |
| if TRITON_AVAILABLE and gate.is_cuda and gate.dtype == torch.bfloat16: | |
| orig_shape = gate.shape | |
| gate_2d = gate.reshape(-1, gate.shape[-1]).contiguous() | |
| up_2d = up.reshape(-1, up.shape[-1]).contiguous() | |
| N, D = gate_2d.shape | |
| out = torch.empty_like(gate_2d) | |
| BLOCK_D = min(triton.next_power_of_2(D), 512) | |
| grid = (N,) | |
| _swiglu_fwd_kernel[grid](gate_2d, up_2d, out, N, D, BLOCK_D=BLOCK_D) | |
| return out.reshape(orig_shape) | |
| else: | |
| return F.silu(gate) * up | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # COMPLEX SSM PARALLEL SCAN (Triton) | |
| # Implements O(T) parallel prefix scan for the ARG recurrent branch. | |
| # For complex-valued states stored as (real, imag) interleaved. | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _ssm_forward_scan( | |
| ar: torch.Tensor, # (B, T, d_state) float32 | |
| ai: torch.Tensor, # (B, T, d_state) | |
| bx: torch.Tensor, # (B, T, d_state) = B_bar * x_inner[:,:,:d_state] | |
| h0_real: torch.Tensor, # (B, d_state) | |
| h0_imag: torch.Tensor, # (B, d_state) | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| """TorchScript-compiled sequential SSM scan β zero Python overhead per step.""" | |
| B = ar.size(0) | |
| T = ar.size(1) | |
| d_state = ar.size(2) | |
| h_real = torch.empty(B, T + 1, d_state, dtype=ar.dtype, device=ar.device) | |
| h_imag = torch.empty(B, T + 1, d_state, dtype=ar.dtype, device=ar.device) | |
| h_real[:, 0] = h0_real | |
| h_imag[:, 0] = h0_imag | |
| for t in range(T): | |
| hr = h_real[:, t] | |
| hi = h_imag[:, t] | |
| art = ar[:, t] | |
| ait = ai[:, t] | |
| h_real[:, t + 1] = art * hr - ait * hi + bx[:, t] | |
| h_imag[:, t + 1] = art * hi + ait * hr | |
| return h_real, h_imag | |
| def _ssm_backward_scan( | |
| ar: torch.Tensor, # (B, T, d_state) | |
| ai: torch.Tensor, # (B, T, d_state) | |
| bb: torch.Tensor, # (B, T, d_state) | |
| xi_slice: torch.Tensor, # (B, T, d_state) β x_inner[:,:,:d_state] | |
| cc: torch.Tensor, # (B, T, d_state) | |
| h_real: torch.Tensor, # (B, T+1, d_state) | |
| h_imag: torch.Tensor, # (B, T+1, d_state) | |
| go_scalar: torch.Tensor, # (B, T) | |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: | |
| """TorchScript-compiled reverse accumulation backward β zero Python overhead per step.""" | |
| B = ar.size(0) | |
| T = ar.size(1) | |
| d_state = ar.size(2) | |
| grad_hreal_from_y = go_scalar.unsqueeze(-1) * cc # (B, T, d_state) | |
| grad_ar = torch.zeros_like(ar) | |
| grad_ai = torch.zeros_like(ai) | |
| grad_bb = torch.zeros_like(bb) | |
| grad_xi_ssm = torch.zeros_like(xi_slice) | |
| delta_hr = torch.zeros(B, d_state, dtype=ar.dtype, device=ar.device) | |
| delta_hi = torch.zeros(B, d_state, dtype=ar.dtype, device=ar.device) | |
| for t in range(T - 1, -1, -1): | |
| hr_t = h_real[:, t] | |
| hi_t = h_imag[:, t] | |
| art = ar[:, t] | |
| ait = ai[:, t] | |
| total_hr = grad_hreal_from_y[:, t] + delta_hr | |
| total_hi = delta_hi | |
| grad_ar[:, t] = total_hr * hr_t + total_hi * hi_t | |
| grad_ai[:, t] = -total_hr * hi_t + total_hi * hr_t | |
| grad_bx_t = total_hr | |
| grad_bb[:, t] = grad_bx_t * xi_slice[:, t] | |
| grad_xi_ssm[:, t] = grad_bx_t * bb[:, t] | |
| delta_hr = art * total_hr + ait * total_hi | |
| delta_hi = -ait * total_hr + art * total_hi | |
| grad_h0_real = delta_hr | |
| grad_h0_imag = delta_hi | |
| return grad_ar, grad_ai, grad_bb, grad_xi_ssm, grad_h0_real, grad_h0_imag | |
| class ComplexSSMScan(torch.autograd.Function): | |
| """ | |
| Fused complex SSM scan with TorchScript forward + analytic backward. | |
| Both the forward scan and backward accumulation loops run inside | |
| @torch.jit.script functions β no Python interpreter overhead per timestep. | |
| """ | |
| def forward(ctx, | |
| x_inner: torch.Tensor, # (B, T, d_inner) | |
| A_bar_real: torch.Tensor, # (B, T, d_state) | |
| A_bar_imag: torch.Tensor, # (B, T, d_state) | |
| B_bar: torch.Tensor, # (B, T, d_state) | |
| C: torch.Tensor, # (B, T, d_state) | |
| D: torch.Tensor, # (d_inner,) | |
| h0_real: torch.Tensor, # (B, d_state) | |
| h0_imag: torch.Tensor, # (B, d_state) | |
| ): | |
| B, T, d_inner = x_inner.shape | |
| d_state = A_bar_real.shape[2] | |
| dtype = x_inner.dtype | |
| ar = A_bar_real.float() | |
| ai = A_bar_imag.float() | |
| bb = B_bar.float() | |
| cc = C.float() | |
| xi = x_inner.float() | |
| D_f = D.float() | |
| bx = bb * xi[:, :, :d_state] # (B, T, d_state) | |
| # TorchScript-compiled forward scan β no Python loop overhead | |
| h_real, h_imag = _ssm_forward_scan(ar, ai, bx, h0_real.float(), h0_imag.float()) | |
| y_ssm = (cc * h_real[:, 1:]).sum(-1, keepdim=True) # (B, T, 1) | |
| y = y_ssm.expand(B, T, d_inner) + D_f.unsqueeze(0).unsqueeze(0) * xi | |
| ctx.save_for_backward(ar, ai, bb, cc, xi, D_f, h_real, h_imag) | |
| ctx.input_dtype = dtype | |
| ctx.d_state = d_state | |
| return y.to(dtype) | |
| def backward(ctx, grad_output): | |
| ar, ai, bb, cc, xi, D_f, h_real, h_imag = ctx.saved_tensors | |
| B, T, d_inner = grad_output.shape | |
| d_state = ctx.d_state | |
| go = grad_output.float() | |
| grad_D = (go * xi).sum([0, 1]) | |
| grad_xi = go * D_f.unsqueeze(0).unsqueeze(0) | |
| go_scalar = go.sum(-1) # (B, T) | |
| grad_C = go_scalar.unsqueeze(-1) * h_real[:, 1:] # (B, T, d_state) | |
| xi_slice = xi[:, :, :d_state] | |
| # TorchScript-compiled backward scan β no Python loop overhead | |
| grad_ar, grad_ai, grad_bb, grad_xi_ssm, grad_h0_real, grad_h0_imag = \ | |
| _ssm_backward_scan(ar, ai, bb, xi_slice, cc, h_real, h_imag, go_scalar) | |
| grad_xi[:, :, :d_state] = grad_xi[:, :, :d_state] + grad_xi_ssm | |
| dtype = ctx.input_dtype | |
| return ( | |
| grad_xi.to(dtype), | |
| grad_ar.to(dtype), | |
| grad_ai.to(dtype), | |
| grad_bb.to(dtype), | |
| grad_C.to(dtype), | |
| grad_D.to(dtype), | |
| grad_h0_real.to(dtype), | |
| grad_h0_imag.to(dtype), | |
| ) | |
| # Export | |
| def complex_ssm_scan(x_inner, A_bar_real, A_bar_imag, B_bar, C, D, h0_real, h0_imag): | |
| """Main entry point for complex SSM scan.""" | |
| return ComplexSSMScan.apply(x_inner, A_bar_real, A_bar_imag, B_bar, C, D, h0_real, h0_imag) |