finish segment_logsumexp #129

This commit is contained in:
gsd 2025-12-09 11:47:55 +08:00
parent e8d83740df
commit eacaa42946
4 changed files with 404 additions and 0 deletions

137
S1/gsd123_#129/prompt.txt Normal file
View File

@ -0,0 +1,137 @@
You write custom CUDA kernels to replace the pytorch operators in the given GeGLU architecture to get speedups.
You have complete freedom to choose the set of operators you want to replace. You may make the decision to replace some operators with custom CUDA kernels and leave others unchanged. You may replace multiple operators with custom implementations, consider operator fusion opportunities (combining multiple operators into a single kernel, for example, combining chunk+gelu+elementwise_mul), or algorithmic changes (such as optimized memory access patterns). You are only limited by your imagination.
Core Mathematical Algorithm
Log-Sum-Exp (LSE) Numerical Stabilization
Formula: log(∑ exp(x_i)) = m + log(∑ exp(x_i - m))
Uses segment-wise maximum m for numerical stability
Prevents overflow in exponential computations
CUDA Implementation Details
Four-Kernel Pipeline Design
init_max_kernel: Initializes maximum values to -1e38
segment_max_kernel: Finds per-segment maximum values
segment_sum_exp_kernel: Computes ∑exp(x_i - m)
finalize_lse_kernel: Finalizes LSE: m + log(sum_exp)
Custom Atomic Float Operations
Reused atomicMaxFloat from previous implementation
Uses atomicCAS for thread-safe float maximum
atomicAdd for accumulating exponential sums
Memory Management Strategy
Three intermediate tensors:
max_val: Per-segment maximum values
sum_exp: Sum of exponentials (offset by max)
out: Final logsumexp results
torch::empty for uninitialized tensors (performance)
torch::zeros for accumulation buffer (safety)
Numerical Computing Techniques
Stable Exponential Computation
Offset by maximum: expf(val - m)
Uses expf and logf CUDA math intrinsics
Initialization to -1e38 instead of -FLT_MAX
Parallel Reduction Pattern
Segment-wise reduction with atomic operations
Two-pass approach: max reduction → sum reduction
Channel-wise independent computation
Performance Optimization
Kernel Fusion/Optimization
Shared index computation between kernels
Reused grid/block calculations
Efficient 2D→1D indexing: segment_id * channels + col
CUDA Best Practices
__restrict__ keyword for compiler optimization
Grid-stride loops with 256 threads per block
Coalesced memory access patterns
PyTorch Integration
Extension Framework
Runtime compilation via load_inline
Automatic device/dtype propagation
Seamless autograd integration
Module Design
nn.Module wrapper for reusability
Configurable dim_size parameter
Clean Python-CUDA interface
Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is:
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self, dim_size):
super(Model, self).__init__()
self.dim_size = dim_size
def forward(self, src, index):
max_val = torch.full((self.dim_size, src.size(1)), -float('inf'), device=src.device, dtype=src.dtype)
index_expanded = index.unsqueeze(1).expand_as(src)
max_val.scatter_reduce_(0, index_expanded, src, reduce='amax', include_self=False)
gathered_max = max_val.gather(0, index_expanded)
exp_src = torch.exp(src - gathered_max)
sum_exp = torch.zeros_like(max_val)
sum_exp.scatter_add_(0, index_expanded, exp_src)
return max_val + torch.log(sum_exp)
batch_size = 1024
features = 64
dim_size = 128
def get_inputs():
src = torch.randn(batch_size, features)
index = torch.randint(0, dim_size, (batch_size,))
return [src, index]
def get_init_inputs():
return [dim_size]

View File

@ -0,0 +1,77 @@
###########################################################
# 性能和精度验证程序
###########################################################
import torch
import torch.nn as nn
import time
from segmentlogsumexp_torch import Model, get_inputs, get_init_inputs
from segmentlogsumexp_cuda import ModelNew
def run_benchmark():
# 检查 CUDA 是否可用
if not torch.cuda.is_available():
print("CUDA 不可用,请确保您有可用的 NVIDIA GPU 并已正确安装 PyTorch CUDA 版本。")
return
else:
device = torch.device("cuda")
# 初始化模型
init_inputs = get_init_inputs()
init_inputs = [
x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs
]
inputs = get_inputs()
inputs = [
x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs
]
torch_model = Model(*init_inputs).cuda()
cuda_model = ModelNew(*init_inputs).cuda()
torch_model.eval()
cuda_model.eval()
print("-------------------- 精度对齐验证 --------------------")
with torch.no_grad():
output_torch = torch_model(*inputs)
output_cuda = cuda_model(*inputs)
precision_flag = torch.allclose(output_torch, output_cuda, rtol=1e-03)
if precision_flag:
print("✅ 精度对齐:两个模型的输出结果非常接近。")
else:
print("❌ 精度不一致!")
print("\n-------------------- 性能加速比测试 --------------------")
num_iterations = 100
# PyTorch 模型计时
torch.cuda.synchronize()
start_time = time.time()
for _ in range(num_iterations):
_ = torch_model(*inputs)
torch.cuda.synchronize()
torch_time = (time.time() - start_time) / num_iterations
# 自定义 CUDA 内核计时
torch.cuda.synchronize()
start_time = time.time()
for _ in range(num_iterations):
_ = cuda_model(*inputs)
torch.cuda.synchronize()
cuda_time = (time.time() - start_time) / num_iterations
print(f"PyTorch torch.relu 平均执行时间: {torch_time:.6f}")
print(f"自定义 CUDA 内核 平均执行时间: {cuda_time:.6f}")
speedup = 0
if cuda_time > 0:
speedup = torch_time / cuda_time
print(f"加速比 (Speedup): {speedup:.2f}x")
else:
print("CUDA 内核执行时间为0无法计算加速比。")
return precision_flag, speedup
if __name__ == "__main__":
precision_flag, speedup = run_benchmark()

View File

@ -0,0 +1,153 @@
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
cuda_source = """
#include <torch/extension.h>
#include <cuda_runtime.h>
#include <cfloat>
__device__ __forceinline__ void atomicMaxFloat(float* address, float val) {
int* address_as_i = (int*)address;
int old = *address_as_i, assumed;
do {
assumed = old;
float old_val = __int_as_float(assumed);
float new_val = fmaxf(val, old_val);
if (new_val == old_val) break;
old = atomicCAS(address_as_i, assumed, __float_as_int(new_val));
} while (assumed != old);
}
__global__ void init_max_kernel(float* out, int size, float val) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < size) {
out[idx] = val;
}
}
__global__ void segment_max_kernel(
const float* __restrict__ src,
const long* __restrict__ index,
float* __restrict__ max_val,
int num_elements,
int channels,
int dim_size
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < num_elements) {
int row = idx / channels;
int col = idx % channels;
long segment_id = index[row];
if (segment_id >= 0 && segment_id < dim_size) {
int out_idx = segment_id * channels + col;
atomicMaxFloat(&max_val[out_idx], src[idx]);
}
}
}
__global__ void segment_sum_exp_kernel(
const float* __restrict__ src,
const long* __restrict__ index,
const float* __restrict__ max_val,
float* __restrict__ sum_exp,
int num_elements,
int channels,
int dim_size
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < num_elements) {
int row = idx / channels;
int col = idx % channels;
long segment_id = index[row];
if (segment_id >= 0 && segment_id < dim_size) {
int out_idx = segment_id * channels + col;
float m = max_val[out_idx];
float val = src[idx];
atomicAdd(&sum_exp[out_idx], expf(val - m));
}
}
}
__global__ void finalize_lse_kernel(
float* __restrict__ out,
const float* __restrict__ max_val,
const float* __restrict__ sum_exp,
int size
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < size) {
float m = max_val[idx];
float s = sum_exp[idx];
out[idx] = m + logf(s);
}
}
torch::Tensor segment_logsumexp_cuda(torch::Tensor src, torch::Tensor index, int dim_size) {
int N = src.size(0);
int C = src.size(1);
int num_elements = N * C;
int out_elements = dim_size * C;
auto max_val = torch::empty({dim_size, C}, src.options());
auto sum_exp = torch::zeros({dim_size, C}, src.options());
auto out = torch::empty({dim_size, C}, src.options());
const int block_size = 256;
int grid_init = (out_elements + block_size - 1) / block_size;
init_max_kernel<<<grid_init, block_size>>>(max_val.data_ptr<float>(), out_elements, -1e38f);
int grid_scatter = (num_elements + block_size - 1) / block_size;
segment_max_kernel<<<grid_scatter, block_size>>>(
src.data_ptr<float>(),
index.data_ptr<long>(),
max_val.data_ptr<float>(),
num_elements,
C,
dim_size
);
segment_sum_exp_kernel<<<grid_scatter, block_size>>>(
src.data_ptr<float>(),
index.data_ptr<long>(),
max_val.data_ptr<float>(),
sum_exp.data_ptr<float>(),
num_elements,
C,
dim_size
);
finalize_lse_kernel<<<grid_init, block_size>>>(
out.data_ptr<float>(),
max_val.data_ptr<float>(),
sum_exp.data_ptr<float>(),
out_elements
);
return out;
}
"""
cpp_source = """
torch::Tensor segment_logsumexp_cuda(torch::Tensor src, torch::Tensor index, int dim_size);
"""
segment_lse_lib = load_inline(
name="segment_logsumexp",
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=["segment_logsumexp_cuda"],
verbose=True
)
class ModelNew(nn.Module):
def __init__(self, dim_size):
super(ModelNew, self).__init__()
self.dim_size = dim_size
self.lib = segment_lse_lib
def forward(self, src, index):
return self.lib.segment_logsumexp_cuda(src, index, self.dim_size)

View File

@ -0,0 +1,37 @@
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self, dim_size):
super(Model, self).__init__()
self.dim_size = dim_size
def forward(self, src, index):
max_val = torch.full((self.dim_size, src.size(1)), -float('inf'), device=src.device, dtype=src.dtype)
index_expanded = index.unsqueeze(1).expand_as(src)
max_val.scatter_reduce_(0, index_expanded, src, reduce='amax', include_self=False)
gathered_max = max_val.gather(0, index_expanded)
exp_src = torch.exp(src - gathered_max)
sum_exp = torch.zeros_like(max_val)
sum_exp.scatter_add_(0, index_expanded, exp_src)
return max_val + torch.log(sum_exp)
batch_size = 1024
features = 64
dim_size = 128
def get_inputs():
src = torch.randn(batch_size, features)
index = torch.randint(0, dim_size, (batch_size,))
return [src, index]
def get_init_inputs():
return [dim_size]