GPUCodeForces/S1/40/SmoothL1Loss_cuda.py

195 lines
5.9 KiB
Python

import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
N, C, H, W = 32, 64, 56, 56
class ModelNew(nn.Module):
def __init__(self, reduction='mean', beta=1.0):
super().__init__()
self.reduction = reduction
self.beta = float(beta)
self.red_map = {'none': 0, 'mean': 1, 'sum': 2}
if reduction not in self.red_map:
raise ValueError("Invalid reduction")
self.reduction_id = self.red_map[reduction]
self.block_size = 256
self._compile_cuda_kernel()
def _compile_cuda_kernel(self):
cpp_source = """
#include <torch/extension.h>
torch::Tensor smooth_l1_forward_cuda(
torch::Tensor input,
torch::Tensor target,
float beta,
int reduction);
"""
cuda_source = """
#include <cuda_runtime.h>
#include <cmath>
#define BLOCK_SIZE 256
#define WARP_SIZE 32
__inline__ __device__ float warp_reduce_sum(float val) {
#pragma unroll
for (int offset = 16; offset > 0; offset /= 2) {
val += __shfl_down_sync(0xffffffff, val, offset);
}
return val;
}
__inline__ __device__ float block_reduce_sum(float val) {
__shared__ float shared[32];
int lane = threadIdx.x % 32;
int wid = threadIdx.x / 32;
val = warp_reduce_sum(val);
if (lane == 0) shared[wid] = val;
__syncthreads();
val = (threadIdx.x < blockDim.x / 32) ? shared[lane] : 0.0f;
if (wid == 0) val = warp_reduce_sum(val);
return val;
}
__global__ void smooth_l1_kernel(
const float* __restrict__ input,
const float* __restrict__ target,
float* __restrict__ output,
int n,
float beta,
int reduction
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int stride = blockDim.x * gridDim.x;
float local_sum = 0.0f;
float4* in_ptr = (float4*)input;
float4* tgt_ptr = (float4*)target;
float4* out_ptr = (float4*)output;
int vec_n = n / 4;
for (int i = idx; i < vec_n; i += stride) {
float4 in_val = in_ptr[i];
float4 tgt_val = tgt_ptr[i];
float4 out_val;
float diff[4];
diff[0] = fabsf(in_val.x - tgt_val.x);
diff[1] = fabsf(in_val.y - tgt_val.y);
diff[2] = fabsf(in_val.z - tgt_val.z);
diff[3] = fabsf(in_val.w - tgt_val.w);
float losses[4];
#pragma unroll
for(int k=0; k<4; ++k) {
if (diff[k] < beta) {
losses[k] = 0.5f * diff[k] * diff[k] / beta;
} else {
losses[k] = diff[k] - 0.5f * beta;
}
}
if (reduction == 0) {
out_val.x = losses[0];
out_val.y = losses[1];
out_val.z = losses[2];
out_val.w = losses[3];
out_ptr[i] = out_val;
} else {
local_sum += losses[0] + losses[1] + losses[2] + losses[3];
}
}
int rem_start = vec_n * 4;
for (int i = rem_start + idx; i < n; i += stride) {
float d = fabsf(input[i] - target[i]);
float l;
if (d < beta) {
l = 0.5f * d * d / beta;
} else {
l = d - 0.5f * beta;
}
if (reduction == 0) {
output[i] = l;
} else {
local_sum += l;
}
}
if (reduction != 0) {
local_sum = block_reduce_sum(local_sum);
if (threadIdx.x == 0) {
atomicAdd(output, local_sum);
}
}
}
torch::Tensor smooth_l1_forward_cuda(
torch::Tensor input,
torch::Tensor target,
float beta,
int reduction)
{
int64_t n = input.numel();
auto options = input.options();
torch::Tensor output;
if (reduction == 0) {
output = torch::empty_like(input);
} else {
output = torch::zeros({1}, options);
}
const int block_size = 256;
const int grid_size = std::min((int)((n + block_size * 4 - 1) / (block_size * 4)), 1024);
smooth_l1_kernel<<<grid_size, block_size>>>(
input.data_ptr<float>(),
target.data_ptr<float>(),
output.data_ptr<float>(),
n,
beta,
reduction
);
if (reduction == 1) {
output.div_(n);
}
return output;
}
"""
self.op = load_inline(
name='smooth_l1_cuda_opt',
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=['smooth_l1_forward_cuda'],
extra_cuda_cflags=['-O3', '--use_fast_math'],
verbose=False
)
def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
if not input.is_cuda: input = input.cuda()
if not target.is_cuda: target = target.cuda()
input = input.contiguous()
target = target.contiguous()
return self.op.smooth_l1_forward_cuda(
input,
target,
self.beta,
self.reduction_id
)