forked from ccf-ai-infra/GPUCodeForces
158 lines
5.6 KiB
Python
158 lines
5.6 KiB
Python
# softmax_cuda.py
|
|
import torch
|
|
import torch.nn as nn
|
|
from torch.utils.cpp_extension import load_inline
|
|
|
|
from softmax_torch import feature_dim
|
|
|
|
assert feature_dim % 4 == 0, "Feature dimension must be a multiple of 4 for float4 vectorization"
|
|
|
|
class ModelNew(nn.Module):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._compile_cuda_kernel()
|
|
|
|
def _compile_cuda_kernel(self):
|
|
cpp_source = """
|
|
#include <torch/extension.h>
|
|
torch::Tensor softmax_forward_cuda(torch::Tensor input);
|
|
"""
|
|
|
|
cuda_source = f"""
|
|
#include <cuda_runtime.h>
|
|
#include <float.h>
|
|
|
|
#define BLOCK_SIZE 512
|
|
#define WARP_SIZE 32
|
|
|
|
__device__ __forceinline__ float warp_reduce_max(float val) {{
|
|
for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2)
|
|
val = fmaxf(val, __shfl_down_sync(0xffffffff, val, offset));
|
|
return val;
|
|
}}
|
|
|
|
__device__ __forceinline__ float warp_reduce_sum(float val) {{
|
|
for (int offset = WARP_SIZE / 2; offset > 0; offset /= 2)
|
|
val += __shfl_down_sync(0xffffffff, val, offset);
|
|
return val;
|
|
}}
|
|
|
|
// Optimized single-pass fused Softmax kernel with float4 vectorization
|
|
__global__ void softmax_fused_vectorized_kernel(
|
|
const float* __restrict__ input,
|
|
float* __restrict__ output,
|
|
int batch_size,
|
|
int feature_dim
|
|
) {{
|
|
extern __shared__ float sdata[];
|
|
float* s_reducers = sdata;
|
|
float* s_x_cache = &sdata[BLOCK_SIZE / WARP_SIZE];
|
|
|
|
const int feature_dim_div4 = feature_dim / 4;
|
|
|
|
int row = blockIdx.x;
|
|
if (row >= batch_size) return;
|
|
|
|
|
|
const float4* x4 = reinterpret_cast<const float4*>(input + row * feature_dim);
|
|
float4* y4 = reinterpret_cast<float4*>(output + row * feature_dim);
|
|
|
|
|
|
for (int i_vec = threadIdx.x; i_vec < feature_dim_div4; i_vec += BLOCK_SIZE) {{
|
|
float4 val4 = x4[i_vec];
|
|
float* cache_ptr = s_x_cache + i_vec * 4;
|
|
|
|
cache_ptr[0] = val4.x;
|
|
cache_ptr[1] = val4.y;
|
|
cache_ptr[2] = val4.z;
|
|
cache_ptr[3] = val4.w;
|
|
}}
|
|
__syncthreads();
|
|
|
|
|
|
float thread_max = -FLT_MAX;
|
|
for (int i = threadIdx.x; i < feature_dim; i += BLOCK_SIZE) {{
|
|
thread_max = fmaxf(thread_max, s_x_cache[i]);
|
|
}}
|
|
|
|
float warp_max = warp_reduce_max(thread_max);
|
|
int warp_id = threadIdx.x / WARP_SIZE;
|
|
int lane_id = threadIdx.x % WARP_SIZE;
|
|
if (lane_id == 0) s_reducers[warp_id] = warp_max;
|
|
__syncthreads();
|
|
|
|
thread_max = (threadIdx.x < BLOCK_SIZE / WARP_SIZE) ? s_reducers[lane_id] : -FLT_MAX;
|
|
if (warp_id == 0) warp_max = warp_reduce_max(thread_max);
|
|
|
|
if (threadIdx.x == 0) s_reducers[0] = warp_max;
|
|
__syncthreads();
|
|
float row_max = s_reducers[0];
|
|
|
|
float thread_sum = 0.0f;
|
|
for (int i = threadIdx.x; i < feature_dim; i += BLOCK_SIZE) {{
|
|
thread_sum += expf(s_x_cache[i] - row_max);
|
|
}}
|
|
|
|
float warp_sum = warp_reduce_sum(thread_sum);
|
|
if (lane_id == 0) s_reducers[warp_id] = warp_sum;
|
|
__syncthreads();
|
|
|
|
thread_sum = (threadIdx.x < BLOCK_SIZE / WARP_SIZE) ? s_reducers[lane_id] : 0.0f;
|
|
if (warp_id == 0) warp_sum = warp_reduce_sum(thread_sum);
|
|
|
|
if (threadIdx.x == 0) s_reducers[0] = warp_sum;
|
|
__syncthreads();
|
|
float row_sum = s_reducers[0];
|
|
float inv_row_sum = 1.0f / row_sum;
|
|
|
|
for (int i_vec = threadIdx.x; i_vec < feature_dim_div4; i_vec += BLOCK_SIZE) {{
|
|
float* cache_ptr = s_x_cache + i_vec * 4;
|
|
|
|
float4 val4;
|
|
|
|
// Read 4 scalars from shared memory and calculate
|
|
val4.x = expf(cache_ptr[0] - row_max) * inv_row_sum;
|
|
val4.y = expf(cache_ptr[1] - row_max) * inv_row_sum;
|
|
val4.z = expf(cache_ptr[2] - row_max) * inv_row_sum;
|
|
val4.w = expf(cache_ptr[3] - row_max) * inv_row_sum;
|
|
|
|
y4[i_vec] = val4;
|
|
}}
|
|
}}
|
|
|
|
torch::Tensor softmax_forward_cuda(torch::Tensor input) {{
|
|
input = input.contiguous();
|
|
int batch_size = input.size(0);
|
|
int feature_dim = input.size(1);
|
|
if (feature_dim % 4 != 0) {{
|
|
AT_ERROR("Feature dimension must be a multiple of 4 for this kernel.");
|
|
}}
|
|
auto output = torch::empty_like(input);
|
|
|
|
const int threads = BLOCK_SIZE;
|
|
const int blocks = batch_size;
|
|
|
|
size_t shared_mem_size = (BLOCK_SIZE / WARP_SIZE + feature_dim) * sizeof(float);
|
|
|
|
softmax_fused_vectorized_kernel<<<blocks, threads, shared_mem_size>>>(
|
|
input.data_ptr<float>(),
|
|
output.data_ptr<float>(),
|
|
batch_size,
|
|
feature_dim
|
|
);
|
|
return output;
|
|
}}
|
|
"""
|
|
|
|
self.softmax_op = load_inline(
|
|
name="softmax_fused_vectorized_op",
|
|
cpp_sources=cpp_source,
|
|
cuda_sources=cuda_source,
|
|
functions=["softmax_forward_cuda"],
|
|
extra_cuda_cflags=["-O3", "--use_fast_math"],
|
|
verbose=False
|
|
)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
return self.softmax_op.softmax_forward_cuda(x.contiguous()) |