-
Notifications
You must be signed in to change notification settings - Fork 69
[WS1] Add CUDA and Triton SiLU/SwiGLU activation kernels #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
maxiaosong1124
merged 6 commits into
RL-Align:main
from
maxiaosong1124:feat/ws1-silu-swiglu-cuda-triton
Aug 9, 2026
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
cd1cf80
feat(ws1): add CUDA and Triton SiLU/SwiGLU activation kernels
maxiaosong1124 c9b58cb
fix(ws1): harden SiLU and SwiGLU kernels
maxiaosong1124 bbce2bf
Merge branch 'RL-Align:main' into feat/ws1-silu-swiglu-cuda-triton
maxiaosong1124 e9f6c62
fix(ws1): address PR #280 review and GPU CI collection failure
maxiaosong1124 5a9220b
chore(ws1): leave WS2 integration test unchanged
maxiaosong1124 d5fd622
Merge branch 'main' into feat/ws1-silu-swiglu-cuda-triton
maxiaosong1124 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,253 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // Copyright (c) 2026 RL-Kernel Contributors | ||
| // | ||
| // Batch-invariant SiLU / SwiGLU CUDA kernels (WS1 elementwise activations). | ||
| // | ||
| // Semantics match NativeSiLUOp / NativeSwiGLUOp: | ||
| // silu(x) = x * sigmoid(x) (math in fp32) | ||
| // swiglu(g, u) = silu(g) * u (math in fp32) | ||
| // | ||
| // Pure elementwise / token-local: no cross-row reduction, so batch size and | ||
| // padding cannot change a row's result (Axis-A bitwise invariance). | ||
|
|
||
| #include <torch/extension.h> | ||
| #include <ATen/cuda/CUDAContext.h> | ||
| #include <c10/cuda/CUDAGuard.h> | ||
| #include <c10/cuda/CUDAException.h> | ||
|
|
||
| namespace { | ||
|
|
||
| __device__ __forceinline__ float silu_f32(float x) { | ||
| // sigmoid(x) = 1 / (1 + exp(-x)); use expf for device fp32. | ||
| const float s = 1.0f / (1.0f + expf(-x)); | ||
| return x * s; | ||
| } | ||
|
|
||
| __device__ __forceinline__ float silu_grad_f32(float x) { | ||
| // d/dx [x * s] = s + x * s * (1 - s) = s * (1 + x * (1 - s)), s = sigmoid(x) | ||
| const float s = 1.0f / (1.0f + expf(-x)); | ||
| return s * (1.0f + x * (1.0f - s)); | ||
| } | ||
|
|
||
| template <typename scalar_t> | ||
| __global__ void silu_forward_kernel( | ||
| const scalar_t* __restrict__ x, | ||
| scalar_t* __restrict__ y, | ||
| const int64_t n) { | ||
| const int64_t idx = blockIdx.x * static_cast<int64_t>(blockDim.x) + threadIdx.x; | ||
| if (idx >= n) { | ||
| return; | ||
| } | ||
| const float xv = static_cast<float>(x[idx]); | ||
| y[idx] = static_cast<scalar_t>(silu_f32(xv)); | ||
| } | ||
|
|
||
| template <typename scalar_t> | ||
| __global__ void silu_backward_kernel( | ||
| const scalar_t* __restrict__ dy, | ||
| const scalar_t* __restrict__ x, | ||
| scalar_t* __restrict__ dx, | ||
| const int64_t n) { | ||
| const int64_t idx = blockIdx.x * static_cast<int64_t>(blockDim.x) + threadIdx.x; | ||
| if (idx >= n) { | ||
| return; | ||
| } | ||
| const float dyv = static_cast<float>(dy[idx]); | ||
| const float xv = static_cast<float>(x[idx]); | ||
| dx[idx] = static_cast<scalar_t>(dyv * silu_grad_f32(xv)); | ||
| } | ||
|
|
||
| template <typename scalar_t> | ||
| __global__ void swiglu_forward_kernel( | ||
| const scalar_t* __restrict__ gate, | ||
| const scalar_t* __restrict__ up, | ||
| scalar_t* __restrict__ y, | ||
| const int64_t n) { | ||
| const int64_t idx = blockIdx.x * static_cast<int64_t>(blockDim.x) + threadIdx.x; | ||
| if (idx >= n) { | ||
| return; | ||
| } | ||
| const float gv = static_cast<float>(gate[idx]); | ||
| const float uv = static_cast<float>(up[idx]); | ||
| y[idx] = static_cast<scalar_t>(silu_f32(gv) * uv); | ||
| } | ||
|
|
||
| template <typename scalar_t> | ||
| __global__ void swiglu_backward_kernel( | ||
| const scalar_t* __restrict__ dy, | ||
| const scalar_t* __restrict__ gate, | ||
| const scalar_t* __restrict__ up, | ||
| scalar_t* __restrict__ d_gate, | ||
| scalar_t* __restrict__ d_up, | ||
| const int64_t n) { | ||
| const int64_t idx = blockIdx.x * static_cast<int64_t>(blockDim.x) + threadIdx.x; | ||
| if (idx >= n) { | ||
| return; | ||
| } | ||
| const float dyv = static_cast<float>(dy[idx]); | ||
| const float gv = static_cast<float>(gate[idx]); | ||
| const float uv = static_cast<float>(up[idx]); | ||
| const float s = silu_f32(gv); | ||
| // d_up = dy * silu(gate); d_gate = dy * up * silu'(gate) | ||
| d_up[idx] = static_cast<scalar_t>(dyv * s); | ||
| d_gate[idx] = static_cast<scalar_t>(dyv * uv * silu_grad_f32(gv)); | ||
| } | ||
|
|
||
| static void launch_1d(int64_t n, int& threads, int64_t& blocks) { | ||
| threads = 256; | ||
| blocks = (n + threads - 1) / threads; | ||
| if (blocks == 0) { | ||
| blocks = 1; | ||
| } | ||
| } | ||
|
|
||
| static void check_cuda_contig(const torch::Tensor& t, const char* name) { | ||
| TORCH_CHECK(t.is_cuda(), name, " must be a CUDA tensor"); | ||
| TORCH_CHECK(t.is_contiguous(), name, " must be contiguous"); | ||
| // Supported activation dtypes only: fp16 / bf16 / fp32 (reject float64). | ||
| TORCH_CHECK( | ||
| t.scalar_type() == at::kHalf || t.scalar_type() == at::kBFloat16 || | ||
| t.scalar_type() == at::kFloat, | ||
| name, | ||
| " must be fp16, bf16, or fp32, got ", | ||
| t.scalar_type()); | ||
| } | ||
|
|
||
| static void check_same_device( | ||
| const torch::Tensor& lhs, | ||
| const torch::Tensor& rhs, | ||
| const char* lhs_name, | ||
| const char* rhs_name) { | ||
| TORCH_CHECK( | ||
| lhs.device() == rhs.device(), | ||
| lhs_name, | ||
| " and ", | ||
| rhs_name, | ||
| " must be on the same CUDA device, got ", | ||
| lhs.device(), | ||
| " and ", | ||
| rhs.device()); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| torch::Tensor silu_forward_cuda(torch::Tensor x) { | ||
| check_cuda_contig(x, "x"); | ||
| const at::cuda::OptionalCUDAGuard device_guard(device_of(x)); | ||
| auto y = torch::empty_like(x); | ||
| const int64_t n = x.numel(); | ||
| if (n == 0) { | ||
| return y; | ||
| } | ||
| int threads = 0; | ||
| int64_t blocks = 0; | ||
| launch_1d(n, threads, blocks); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
|
|
||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::ScalarType::Half, at::ScalarType::BFloat16, x.scalar_type(), "silu_forward_cuda", [&] { | ||
| silu_forward_kernel<scalar_t><<<blocks, threads, 0, stream>>>( | ||
| x.data_ptr<scalar_t>(), y.data_ptr<scalar_t>(), n); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return y; | ||
| } | ||
|
|
||
| torch::Tensor silu_backward_cuda(torch::Tensor dy, torch::Tensor x) { | ||
| check_cuda_contig(dy, "dy"); | ||
| check_cuda_contig(x, "x"); | ||
| check_same_device(dy, x, "dy", "x"); | ||
| TORCH_CHECK(dy.sizes() == x.sizes(), "dy and x must share shape"); | ||
| TORCH_CHECK(dy.scalar_type() == x.scalar_type(), "dy and x must share dtype"); | ||
| const at::cuda::OptionalCUDAGuard device_guard(device_of(x)); | ||
| auto dx = torch::empty_like(x); | ||
| const int64_t n = x.numel(); | ||
| if (n == 0) { | ||
| return dx; | ||
| } | ||
| int threads = 0; | ||
| int64_t blocks = 0; | ||
| launch_1d(n, threads, blocks); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
|
|
||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::ScalarType::Half, at::ScalarType::BFloat16, x.scalar_type(), "silu_backward_cuda", [&] { | ||
| silu_backward_kernel<scalar_t><<<blocks, threads, 0, stream>>>( | ||
| dy.data_ptr<scalar_t>(), x.data_ptr<scalar_t>(), dx.data_ptr<scalar_t>(), n); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return dx; | ||
| } | ||
|
|
||
| torch::Tensor swiglu_forward_cuda(torch::Tensor gate, torch::Tensor up) { | ||
| check_cuda_contig(gate, "gate"); | ||
| check_cuda_contig(up, "up"); | ||
| check_same_device(gate, up, "gate", "up"); | ||
| TORCH_CHECK(gate.sizes() == up.sizes(), "gate and up must share shape"); | ||
| TORCH_CHECK(gate.scalar_type() == up.scalar_type(), "gate and up must share dtype"); | ||
| const at::cuda::OptionalCUDAGuard device_guard(device_of(gate)); | ||
| auto y = torch::empty_like(gate); | ||
| const int64_t n = gate.numel(); | ||
| if (n == 0) { | ||
| return y; | ||
| } | ||
| int threads = 0; | ||
| int64_t blocks = 0; | ||
| launch_1d(n, threads, blocks); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
|
|
||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::ScalarType::Half, | ||
| at::ScalarType::BFloat16, | ||
| gate.scalar_type(), | ||
| "swiglu_forward_cuda", | ||
| [&] { | ||
| swiglu_forward_kernel<scalar_t><<<blocks, threads, 0, stream>>>( | ||
| gate.data_ptr<scalar_t>(), up.data_ptr<scalar_t>(), y.data_ptr<scalar_t>(), n); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return y; | ||
| } | ||
|
|
||
| std::vector<torch::Tensor> swiglu_backward_cuda( | ||
| torch::Tensor dy, | ||
| torch::Tensor gate, | ||
| torch::Tensor up) { | ||
| check_cuda_contig(dy, "dy"); | ||
| check_cuda_contig(gate, "gate"); | ||
| check_cuda_contig(up, "up"); | ||
| check_same_device(dy, gate, "dy", "gate"); | ||
| check_same_device(gate, up, "gate", "up"); | ||
| TORCH_CHECK(gate.sizes() == up.sizes(), "gate and up must share shape"); | ||
| TORCH_CHECK(dy.sizes() == gate.sizes(), "dy and gate must share shape"); | ||
| TORCH_CHECK(dy.scalar_type() == gate.scalar_type(), "dy and gate must share dtype"); | ||
| TORCH_CHECK(up.scalar_type() == gate.scalar_type(), "up and gate must share dtype"); | ||
| const at::cuda::OptionalCUDAGuard device_guard(device_of(gate)); | ||
| auto d_gate = torch::empty_like(gate); | ||
| auto d_up = torch::empty_like(up); | ||
| const int64_t n = gate.numel(); | ||
| if (n == 0) { | ||
| return {d_gate, d_up}; | ||
| } | ||
| int threads = 0; | ||
| int64_t blocks = 0; | ||
| launch_1d(n, threads, blocks); | ||
| auto stream = at::cuda::getCurrentCUDAStream(); | ||
|
|
||
| AT_DISPATCH_FLOATING_TYPES_AND2( | ||
| at::ScalarType::Half, | ||
| at::ScalarType::BFloat16, | ||
| gate.scalar_type(), | ||
| "swiglu_backward_cuda", | ||
| [&] { | ||
| swiglu_backward_kernel<scalar_t><<<blocks, threads, 0, stream>>>( | ||
| dy.data_ptr<scalar_t>(), | ||
| gate.data_ptr<scalar_t>(), | ||
| up.data_ptr<scalar_t>(), | ||
| d_gate.data_ptr<scalar_t>(), | ||
| d_up.data_ptr<scalar_t>(), | ||
| n); | ||
| }); | ||
| C10_CUDA_KERNEL_LAUNCH_CHECK(); | ||
| return {d_gate, d_up}; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.