Skip to content

[WS1] Add CUDA and Triton SiLU/SwiGLU activation kernels - #280

Open
maxiaosong1124 wants to merge 5 commits into
RL-Align:mainfrom
maxiaosong1124:feat/ws1-silu-swiglu-cuda-triton
Open

[WS1] Add CUDA and Triton SiLU/SwiGLU activation kernels#280
maxiaosong1124 wants to merge 5 commits into
RL-Align:mainfrom
maxiaosong1124:feat/ws1-silu-swiglu-cuda-triton

Conversation

@maxiaosong1124

@maxiaosong1124 maxiaosong1124 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds CUDA and Triton implementations of SiLU and SwiGLU for the WS1 batch-invariant forward chain.

The new kernels provide deterministic, element-wise forward and backward paths and match the existing PyTorch FP32 ground-truth implementation under the shared #108 numerical contract.

This PR also wires both operators into the kernel registry and the shared operator validation harness.

Part of #271.
Related to #83 and #108.

Implementation

  • Add native CUDA forward and backward kernels for:
    • silu(x) = x * sigmoid(x)
    • swiglu(gate, up) = silu(gate) * up
  • Add equivalent Triton forward and backward kernels.
  • Perform kernel math in FP32 and cast results back to the input dtype for the regular forward path.
  • Provide forward_fp32 as the FP32 ground-truth path.
  • Support FP16, BF16, and FP32 inputs.
  • Support arbitrary tensor shapes, including non-contiguous and empty tensors.
  • Add CUDA extension bindings and type declarations.
  • Register CUDA, Triton, and PyTorch candidates in OP_SPECS.
  • Update backend dispatch:
    • CUDA: CUDA -> Triton -> PyTorch
    • ROCm: Triton -> PyTorch
    • CPU: PyTorch
  • Add operator documentation and usage examples.

Numerical contract and invariance

SiLU and SwiGLU are element-wise and row-independent, so no cross-row reductions or atomics are required.

The tests verify:

  • forward and backward parity against the independent FP32 PyTorch reference;
  • bitwise batch-slice invariance;
  • bitwise padding-layout invariance for outputs and gradients;
  • deterministic repeated forward and backward execution;
  • FP16, BF16, and FP32 dtype behavior;
  • shape, dtype, and device validation for SwiGLU inputs;
  • non-contiguous and empty tensor handling;
  • CUDA and Triton integration with the shared [WS1] Ground-truth harness + numerical contract for batch-invariant ops #108 operator harness.

For SwiGLU, gate and up must have the same shape, dtype, and device. Broadcasting is intentionally unsupported.

Validation

Validation environment:

  • NVIDIA H20
  • CUDA 12.8

Commands and results:

pre-commit run --all-files
# all hooks passed

python -m pytest \
  tests/test_swiglu.py \
  tests/test_op_checks.py \
  tests/test_operator_inputs.py \
  tests/test_kernel_registry.py -q
# 142 passed, 2 skipped

python -m pytest -q
# 1434 passed, 120 skipped

The CUDA extension was rebuilt successfully, and the CUDA/Triton BF16 operator-harness checks passed for both SiLU and SwiGLU.

## Scope

This PR provides standalone SiLU and SwiGLU operators for WS1 correctness and consistency validation.

Fused bias + SiLU and packed SiluAndMul variants are not included. Callers with packed gate/up tensors should split them before invoking SwiGLU.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
- Added SiLU and SwiGLU activation operations with forward and backward support.
- Added CUDA and Triton acceleration for supported floating-point types.
- Added automatic backend selection with PyTorch fallback and optional FP32 execution.

- **Bug Fixes**
- Improved validation for supported dtypes, devices, shapes, and compatible inputs.

- **Documentation**
- Documented backend behavior, accuracy expectations, performance, and known limitations.

- **Tests**
- Expanded coverage for gradients, dispatch, empty and noncontiguous inputs, batching, padding, and dtype validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Implement batch-invariant SiLU and SwiGLU CUDA/Triton backends matching the
PyTorch fp32 gold path, register them in the kernel registry and issue RL-Align#108
OP_SPECS harness, and extend tests for correctness, Axis-A invariance, and
candidate-vs-gold consistency on Qwen3-8B intermediate shapes.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds CUDA and Triton implementations for SiLU and SwiGLU, including forward and backward paths. It exposes CUDA extension APIs, updates backend dispatch and validation, documents contracts, and adds operator and edge-case coverage.

Changes

SiLU and SwiGLU activation backends

Layer / File(s) Summary
CUDA kernels and extension bindings
csrc/cuda/activation.cu, csrc/ops.cpp, rl_engine/_C.pyi, setup.py
Adds CUDA forward and backward kernels, validation, stream-aware launches, PyBind registrations, type stubs, and build integration.
CUDA Python operators
rl_engine/kernels/ops/cuda/...
Adds CUDA exports, dtype and extension checks, custom autograd functions, and standard and fp32 execution paths.
Triton kernels and native validation
rl_engine/kernels/ops/triton/..., rl_engine/kernels/ops/pytorch/...
Adds Triton forward and backward kernels with autograd integration. Native operators validate supported dtypes and matching SwiGLU inputs.
Backend dispatch and operator contracts
rl_engine/kernels/registry.py, rl_engine/kernels/gtest/operator_specs.py, docs/operators/activation.md
Adds backend entries, priority ordering, operator specifications, and documentation for contracts, fallback, accuracy, and limitations.
Activation validation and harness tests
tests/test_swiglu.py, tests/test_qwen3_fwd_integration.py
Adds numerical, gradient, dispatch, determinism, invariance, invalid-input, edge-case, harness, and compatibility coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Registry
  participant CUDAOrTriton
  participant Autograd
  Caller->>Registry: request SiLU or SwiGLU
  Registry->>CUDAOrTriton: select available backend
  CUDAOrTriton->>Autograd: run forward and save inputs
  Autograd-->>CUDAOrTriton: request backward gradients
  CUDAOrTriton-->>Caller: return output or gradients
Loading

Possibly related PRs

  • RL-Align/RL-Kernel#166: Added the SiLU/SwiGLU implementations, registry, documentation, and tests extended by this change.
  • RL-Align/RL-Kernel#243: Extended shared operator registration and dispatch infrastructure for additional kernels.

Suggested reviewers: kjldefeated, ethanzero2hero, bitborne

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding CUDA and Triton SiLU/SwiGLU activation kernels.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
tests/test_swiglu.py (2)

585-585: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a raw string for the regex pattern.

Ruff flags RUF043 here. The pattern contains regex metacharacters. Use a raw string to make the intent explicit and to satisfy lint.

-    with pytest.raises(RuntimeError, match="same .*device"):
+    with pytest.raises(RuntimeError, match=r"same .*device"):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_swiglu.py` at line 585, Update the pytest.raises call’s match
argument in the relevant test to use a raw string literal for the regex pattern,
preserving the existing “same .*device” matching behavior and satisfying Ruff
RUF043.

Source: Linters/SAST tools


528-531: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add strict= to zip().

Ruff flags B905 on line 531. expected and actual are both 3-tuples from _run, so strict=True is safe and makes a future tuple-shape change fail loudly.

-        assert all(torch.equal(lhs, rhs) for lhs, rhs in zip(expected, actual))
+        assert all(torch.equal(lhs, rhs) for lhs, rhs in zip(expected, actual, strict=True))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_swiglu.py` around lines 528 - 531, Update the zip() call in the
test loop around _run() to pass strict=True, preserving the existing equality
assertion while ensuring mismatched expected and actual tuple lengths fail
explicitly.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@csrc/cuda/activation.cu`:
- Around line 104-108: Update check_cuda_contig() to accept only torch.float16,
torch.bfloat16, and torch.float32, rejecting float64 at the native boundary.
Ensure the relevant AT_DISPATCH_FLOATING_TYPES_AND2[...] usage also excludes
double so direct _C calls cannot dispatch float64.

In `@rl_engine/kernels/ops/triton/activation/swiglu.py`:
- Around line 137-139: Update the activation Function.forward implementations
around _launch_silu_fwd and the corresponding SwiGLU forward path to materialize
contiguous input tensors once, pass those tensors to the launch helpers, and
save the same tensors with ctx.save_for_backward. Remove duplicate contiguity
conversions inside the helpers or forward code while preserving the existing
launch and autograd behavior.

In `@tests/test_swiglu.py`:
- Around line 26-40: Guard the module-level TritonSiLUOp and TritonSwiGLUOp
import with an ImportError handler, setting unavailable symbols and a capability
flag consistently with the CUDA block. Update the test parametrization to
include the Triton case only when Triton is available, while preserving native
and CUDA test coverage in environments without Triton.

---

Nitpick comments:
In `@tests/test_swiglu.py`:
- Line 585: Update the pytest.raises call’s match argument in the relevant test
to use a raw string literal for the regex pattern, preserving the existing “same
.*device” matching behavior and satisfying Ruff RUF043.
- Around line 528-531: Update the zip() call in the test loop around _run() to
pass strict=True, preserving the existing equality assertion while ensuring
mismatched expected and actual tuple lengths fail explicitly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dc56ccd2-4372-4e97-a7be-e8eb6a895c00

📥 Commits

Reviewing files that changed from the base of the PR and between 0b12d34 and c9b58cb.

📒 Files selected for processing (14)
  • csrc/cuda/activation.cu
  • csrc/ops.cpp
  • docs/operators/activation.md
  • rl_engine/_C.pyi
  • rl_engine/kernels/gtest/operator_specs.py
  • rl_engine/kernels/ops/cuda/__init__.py
  • rl_engine/kernels/ops/cuda/activation/__init__.py
  • rl_engine/kernels/ops/cuda/activation/swiglu.py
  • rl_engine/kernels/ops/pytorch/activation/swiglu.py
  • rl_engine/kernels/ops/triton/activation/__init__.py
  • rl_engine/kernels/ops/triton/activation/swiglu.py
  • rl_engine/kernels/registry.py
  • setup.py
  • tests/test_swiglu.py

Comment thread csrc/cuda/activation.cu
Comment thread rl_engine/kernels/ops/triton/activation/swiglu.py Outdated
Comment thread tests/test_swiglu.py
Comment thread tests/test_swiglu.py Outdated
operator_names,
)
from rl_engine.kernels.ops.pytorch.activation.swiglu import NativeSiLUOp, NativeSwiGLUOp
from rl_engine.kernels.ops.triton.activation.swiglu import TritonSiLUOp, TritonSwiGLUOp

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This top-level Triton import makes the whole file fail during collection when Triton isn’t installed. Could we guard it with a try/except and skip, like the other Triton tests?

Comment thread tests/test_swiglu.py
]


@requires_nvidia_cuda

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert feels a bit too hard for a CUDA test. If the extension isn’t built yet, this fails instead of skipping even on a valid CUDA setup. Could we make it skip unless those symbols are actually present?

Guard Triton imports and soft-skip missing CUDA symbols so CPU-only and
partial-extension environments can still collect tests. Reuse a single
contiguous materialization in CUDA/Triton autograd forwards, reject float64
at the native boundary, and resolve the Qwen3 integration script's hard
import of SwiGLUSM90Op so pytest collection no longer aborts GPU CI.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/test_qwen3_fwd_integration.py (2)

96-109: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Build the oracle independently of _SwiGLUOp.

hidden_fp32 is converted from hidden_local. A defective SwiGLU implementation can therefore pass this validation because both the tested path and the oracle use the same activation output.

Compute the reference activation from gate_local.float() and up_local.float() with PyTorch SiLU. Apply the expected BF16 boundary cast before the down projection. Assert hidden_local against that reference before validating final_output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_qwen3_fwd_integration.py` around lines 96 - 109, The FP32 oracle
currently reuses hidden_local, so it cannot detect defects in _SwiGLUOp. Build
the reference independently from gate_local.float() and up_local.float() using
PyTorch SiLU, apply the expected BF16 boundary cast before the down projection,
and assert hidden_local matches this reference before the existing final_output
oracle validation.

34-41: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Model the TP and CP data ownership.

x_local uses 42 + rank. Ranks 0 and 1, and ranks 2 and 3, are in the same TP group but receive different activations. The TP all-reduce therefore sums independent forward passes instead of TP shards of one forward pass.

cp_groups is created but never used. Derive TP and CP coordinates. Seed or shard activations by CP coordinate. Seed or shard weights by TP coordinate. Use the CP group in a CP invariant or communication check.

Also applies to: 63-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_qwen3_fwd_integration.py` around lines 34 - 41, Update the
distributed setup and test flow around the TP/CP group construction and forward
inputs so data ownership matches the parallel dimensions: derive each rank’s TP
and CP coordinates, shard or seed activations by CP coordinate, and shard or
seed weights by TP coordinate. Replace the unused cp_groups path with a CP
invariant or communication check, while preserving TP all-reduce validation for
shards belonging to the same forward pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/test_qwen3_fwd_integration.py`:
- Around line 96-109: The FP32 oracle currently reuses hidden_local, so it
cannot detect defects in _SwiGLUOp. Build the reference independently from
gate_local.float() and up_local.float() using PyTorch SiLU, apply the expected
BF16 boundary cast before the down projection, and assert hidden_local matches
this reference before the existing final_output oracle validation.
- Around line 34-41: Update the distributed setup and test flow around the TP/CP
group construction and forward inputs so data ownership matches the parallel
dimensions: derive each rank’s TP and CP coordinates, shard or seed activations
by CP coordinate, and shard or seed weights by TP coordinate. Replace the unused
cp_groups path with a CP invariant or communication check, while preserving TP
all-reduce validation for shards belonging to the same forward pass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd2eda07-820a-4182-927b-69bf73247a25

📥 Commits

Reviewing files that changed from the base of the PR and between c9b58cb and e9f6c62.

📒 Files selected for processing (5)
  • csrc/cuda/activation.cu
  • rl_engine/kernels/ops/cuda/activation/swiglu.py
  • rl_engine/kernels/ops/triton/activation/swiglu.py
  • tests/test_qwen3_fwd_integration.py
  • tests/test_swiglu.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • rl_engine/kernels/ops/triton/activation/swiglu.py
  • rl_engine/kernels/ops/cuda/activation/swiglu.py
  • csrc/cuda/activation.cu
  • tests/test_swiglu.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants