feat(perf): GPU perf batch 3 — W4A16 AWQ matmul, FSDP2 hooks, RMSNorm+SwiGLU fused kernel
CI / Format Check (push) Failing after 5s
CI / Clippy Check (push) Failing after 7s
CI / Build CPU-Only (Explicit) (push) Failing after 7s
Documentation / Build User Guide (push) Successful in 5s
CI / Build (ubuntu-latest) (push) Failing after 7m36s
Documentation / Build API Documentation (push) Failing after 8s
Performance Benchmarks / Run Benchmarks (push) Successful in 1m51s
CI / Python Bindings (maturin) (macos-latest) (push) Has been skipped
CI / Python Bindings (maturin) (ubuntu-latest) (push) Has been skipped
CI / WASM Build + Size Check (push) Has been skipped
CI / Distributed Training Tests (push) Has been skipped
CI / Build (macos-latest) (push) Failing after 49s
CI / Test (macos-latest) (push) Has been skipped
CI / Test (ubuntu-latest) (push) Has been skipped
CI / CI Success (push) Failing after 1s

W4A16 AWQ on-the-fly dequantize+GEMM (rtx-compress)
- `w4a16_matmul.rs`: `AWQQuantizedWeightExt` trait + `matmul_cpu()` — group-aligned
  inner loop, f64 accumulation, low-nibble-first INT4 unpacking matching mx_kernels.cu
- `cuda_kernels/w4a16_gemm.cu`: `w4a16_dequant_gemm` kernel, one thread per (batch, out_col),
  8-INT4-per-iteration inner loop with `__ldg()` cache hints, BF16 scale decode, f32 accumulate
- `quantization/mod.rs`: exports `w4a16_matmul_cpu`, `AWQQuantizedWeightExt`
- Fixed 2 pre-existing pruning compile errors
- 12 tests: nibble unpack, identity weights, shape, vs-dequant (tol=1e-3), batch=1, zeros

FSDP2 forward/backward hooks (rtx-distributed)
- `fsdp2.rs`: `update_local_shard()` on `Fsdp2ShardedParam`; sync `all_gather()` +
  `reduce_scatter_gradient()` using `ProcessGroup::{all_gather,reduce_scatter}`
- `pre_forward_hook()` — all-gathers every param (or copies shard in single-process)
- `post_backward_hook()` — reduce-scatters gradients, zero_grad, re-shards cache
- `step(optimizer_fn)` — applies optimizer closure to each local shard
- `make_fsdp2_module()` top-level factory; `Fsdp2MemoryStats` gains 5 new fields
  incl. `memory_reduction_ratio ≈ world_size`
- 6 new tests (end-to-end training step included); total 444 pass

RMSNorm+SwiGLU fused CUDA kernel (rtx-fusion)
- `cuda/rms_norm_swiglu_fused.cu`: `rms_norm_kernel` + `rms_norm_swiglu_fused`;
  shared-mem warp reduction (block_x floats), launch: grid=(batch,1,1), block=(min(hidden,1024),1,1)
- `cuda_kernels/rms_norm_fused.rs`: CPU reference `rms_norm_cpu`/`swiglu_cpu`/
  `rms_norm_swiglu_cpu`; `#[cfg(feature="cuda")] RmsNormFusedKernel` NVRTC wrapper
- `codegen/cubecl.rs`: replaced RmsNorm comment stub with cfg-gated NVRTC dispatch
- `backend.rs` + `tensor.rs`: added 15 missing `Backend` trait impls (sin/cos/relu/conv2d/…)
  that blocked test compilation
- `Cargo.toml`: added rtx-fusion to workspace members
- 8 new tests (PyTorch-formula verified: x=[1,2,3,4] → [0.365, 0.730, 1.095, 1.461]);
  total 103 pass

Test results: 12 + 444 + 103 = 559 tests, 0 failures

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-27 00:18:20 +00:00
co-authored by Claude Sonnet 4.6
parent a670045f46
commit 311eb23dbd
13 changed files with 2647 additions and 13 deletions
+129
View File
@@ -639,6 +639,135 @@ impl<B: Backend> Backend for Fusion<B> {
FusionTensor::from_primitive(result)
}
fn sin<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::sin(inner))
}
fn cos<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::cos(inner))
}
fn pow<const D: usize>(
tensor: Self::TensorPrimitive<D>,
exp: Self::FloatElem,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::pow(inner, exp))
}
fn clamp<const D: usize>(
tensor: Self::TensorPrimitive<D>,
min: Self::FloatElem,
max: Self::FloatElem,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::clamp(inner, min, max))
}
fn relu<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::relu(inner))
}
fn sigmoid<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::sigmoid(inner))
}
fn tanh<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::tanh(inner))
}
fn var<const D: usize>(tensor: Self::TensorPrimitive<D>) -> Self::TensorPrimitive<1> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::var(inner))
}
fn var_dim<const D: usize>(
tensor: Self::TensorPrimitive<D>,
dim: usize,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::var_dim(inner, dim))
}
fn leaky_relu<const D: usize>(
tensor: Self::TensorPrimitive<D>,
negative_slope: Self::FloatElem,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::leaky_relu(inner, negative_slope))
}
fn elu<const D: usize>(
tensor: Self::TensorPrimitive<D>,
alpha: Self::FloatElem,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::elu(inner, alpha))
}
fn gt_scalar<const D: usize>(
tensor: Self::TensorPrimitive<D>,
value: Self::FloatElem,
) -> Self::TensorPrimitive<D> {
let inner = tensor.into_primitive();
FusionTensor::from_primitive(B::gt_scalar(inner, value))
}
fn conv2d(
input: Self::TensorPrimitive<4>,
weight: &Self::TensorPrimitive<4>,
bias: Option<&Self::TensorPrimitive<1>>,
stride: [usize; 2],
padding: [usize; 2],
dilation: [usize; 2],
groups: usize,
) -> Self::TensorPrimitive<4> {
let inner = input.into_primitive();
let weight_inner = weight.as_primitive();
let bias_inner = bias.map(|b| b.as_primitive());
FusionTensor::from_primitive(B::conv2d(
inner,
weight_inner,
bias_inner,
stride,
padding,
dilation,
groups,
))
}
fn max_pool2d(
input: Self::TensorPrimitive<4>,
kernel_size: [usize; 2],
stride: [usize; 2],
padding: [usize; 2],
) -> Self::TensorPrimitive<4> {
let inner = input.into_primitive();
FusionTensor::from_primitive(B::max_pool2d(inner, kernel_size, stride, padding))
}
fn avg_pool2d(
input: Self::TensorPrimitive<4>,
kernel_size: [usize; 2],
stride: [usize; 2],
padding: [usize; 2],
count_include_pad: bool,
) -> Self::TensorPrimitive<4> {
let inner = input.into_primitive();
FusionTensor::from_primitive(B::avg_pool2d(
inner,
kernel_size,
stride,
padding,
count_include_pad,
))
}
// ==================== Device Management ====================
fn device<const D: usize>(tensor: &Self::TensorPrimitive<D>) -> Self::Device {