forked from ccf-ai-infra/GPUCodeForces
179 lines
6.0 KiB
Python
179 lines
6.0 KiB
Python
import torch
|
|
import torch.nn as nn
|
|
from torch.utils.cpp_extension import load_inline
|
|
|
|
BATCH_SIZE = 4096
|
|
FEATURE_DIM = 512
|
|
|
|
# --- 损失函数的参数 ---
|
|
LOG_INPUT = True
|
|
FULL = False
|
|
EPS = 1e-8
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
|
|
class ModelNew(nn.Module):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
# 将 Python 端的常量存储为实例属性
|
|
self.log_input = LOG_INPUT
|
|
self.full = FULL
|
|
self.eps = EPS
|
|
self._compile_cuda_kernel()
|
|
|
|
def _compile_cuda_kernel(self):
|
|
cpp_source = """
|
|
#include <torch/extension.h>
|
|
|
|
// C++ 接口
|
|
torch::Tensor poisson_nll_forward_cuda(
|
|
torch::Tensor input,
|
|
torch::Tensor target,
|
|
bool log_input,
|
|
bool full,
|
|
float eps_val
|
|
);
|
|
"""
|
|
cuda_source = """
|
|
#include <torch/extension.h>
|
|
#include <cuda_runtime.h>
|
|
#include <cmath> // for logf, expf
|
|
#include <float.h>
|
|
|
|
// 块大小
|
|
#define BLOCK_SIZE 256
|
|
// 定义 PI
|
|
const float PI = 3.141592653589793f;
|
|
|
|
/*
|
|
* PoissonNLLLoss 融合核函数
|
|
* 这是一个标准的并行归约核函数。
|
|
* 每个线程处理多个元素 (Grid-Stride Loop),计算它们的 loss 并累加。
|
|
* 然后执行一个块内归约 (Block-level reduction)。
|
|
* C++ host 端对所有块的和再次求和,然后除以 N 得到 'mean'。
|
|
*/
|
|
__global__ void poisson_nll_fused_kernel(
|
|
const float* __restrict__ input_data,
|
|
const float* __restrict__ target_data,
|
|
float* __restrict__ block_loss_sums_out, // (grid_size,)
|
|
int n_elements,
|
|
bool log_input,
|
|
bool full,
|
|
float eps_val
|
|
) {
|
|
__shared__ float s_data[BLOCK_SIZE];
|
|
|
|
float thread_loss_sum = 0.0f;
|
|
int grid_stride = gridDim.x * blockDim.x;
|
|
|
|
// Grid-Stride Loop 遍历所有元素
|
|
for (int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
idx < n_elements;
|
|
idx += grid_stride)
|
|
{
|
|
float x = input_data[idx]; // 'input'
|
|
float y = target_data[idx]; // 'target'
|
|
float loss_val = 0.0f;
|
|
|
|
// --- 核心 Loss 计算 ---
|
|
if (log_input) {
|
|
// loss = exp(input) - target * input
|
|
loss_val = expf(x) - y * x;
|
|
} else {
|
|
// loss = input - target * log(input + eps)
|
|
loss_val = x - y * logf(x + eps_val);
|
|
}
|
|
|
|
// --- 'full' 模式的附加项 ---
|
|
// (target * log(target) - target + 0.5 * log(2 * pi * target))
|
|
if (full && y > 1.0f) {
|
|
float stirling_term = y * logf(y) - y + 0.5f * logf(2.0f * PI * y);
|
|
loss_val += stirling_term;
|
|
}
|
|
|
|
// 累加该线程处理的所有元素的 loss
|
|
thread_loss_sum += loss_val;
|
|
}
|
|
|
|
// --- 块内归约 (Sum) ---
|
|
s_data[threadIdx.x] = thread_loss_sum;
|
|
__syncthreads();
|
|
|
|
for (int offset = BLOCK_SIZE / 2; offset > 0; offset >>= 1) {
|
|
if (threadIdx.x < offset) {
|
|
s_data[threadIdx.x] += s_data[threadIdx.x + offset];
|
|
}
|
|
__syncthreads();
|
|
}
|
|
|
|
// 块中的第一个线程将块的总和写入全局内存
|
|
if (threadIdx.x == 0) {
|
|
block_loss_sums_out[blockIdx.x] = s_data[0];
|
|
}
|
|
}
|
|
|
|
// C++ 封装函数
|
|
torch::Tensor poisson_nll_forward_cuda(
|
|
torch::Tensor input,
|
|
torch::Tensor target,
|
|
bool log_input,
|
|
bool full,
|
|
float eps_val
|
|
) {
|
|
// 检查
|
|
TORCH_CHECK(input.is_cuda(), "input must be a CUDA tensor");
|
|
TORCH_CHECK(target.is_cuda(), "target must be a CUDA tensor");
|
|
|
|
input = input.contiguous();
|
|
target = target.contiguous();
|
|
|
|
const int n_elements = input.numel();
|
|
TORCH_CHECK(target.numel() == n_elements, "input and target must have the same number of elements");
|
|
|
|
if (n_elements == 0) {
|
|
return torch::tensor(0.0f, input.options());
|
|
}
|
|
|
|
// 分配一个张量来保存每个块的部分和
|
|
const int block_size = BLOCK_SIZE;
|
|
const int grid_size = std::max(1, (n_elements + block_size - 1) / block_size);
|
|
auto block_loss_sums = torch::empty({grid_size}, input.options());
|
|
|
|
// 启动 CUDA 核函数
|
|
poisson_nll_fused_kernel<<<grid_size, block_size>>>(
|
|
input.data_ptr<float>(),
|
|
target.data_ptr<float>(),
|
|
block_loss_sums.data_ptr<float>(),
|
|
n_elements,
|
|
log_input,
|
|
full,
|
|
eps_val
|
|
);
|
|
|
|
// 核函数返回后,对所有块的和进行求和,然后除以总元素数
|
|
// (reduction='mean')
|
|
return block_loss_sums.sum() / n_elements;
|
|
}
|
|
"""
|
|
|
|
# JIT (Just-In-Time) 编译
|
|
self.pnl_op = load_inline(
|
|
name="poisson_nll_op_v1",
|
|
cpp_sources=cpp_source,
|
|
cuda_sources=cuda_source,
|
|
functions=["poisson_nll_forward_cuda"],
|
|
extra_cuda_cflags=["-O3"],
|
|
verbose=False
|
|
)
|
|
|
|
def forward(self, input_tensor: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
|
|
# 调用我们编译好的 CUDA C++ 函数
|
|
return self.pnl_op.poisson_nll_forward_cuda(
|
|
input_tensor,
|
|
target,
|
|
self.log_input,
|
|
self.full,
|
|
self.eps
|
|
) |