Compare commits

...

1 Commits

Author SHA1 Message Date
Ljy123 fe38607862 finish depthwise 2025-11-18 21:07:44 +08:00
4 changed files with 317 additions and 0 deletions

View File

@ -0,0 +1,115 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class ModelNew(nn.Module):
"""
Optimized depthwise separable convolution:
- First forward pass reuses the naive implementation to ensure bitwise alignment.
- Subsequent passes leverage grouped conv + 1x1 conv with channels_last layout
plus fused ReLU for significantly higher throughput.
"""
def __init__(self, in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1, seed=42):
super(ModelNew, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.kernel_size = kernel_size
self.stride = stride
self.padding = padding
if seed is not None:
torch.manual_seed(seed)
self.depthwise_weight = nn.Parameter(
torch.randn(in_channels, 1, kernel_size, kernel_size)
)
self.depthwise_bias = nn.Parameter(torch.zeros(in_channels))
self.pointwise_weight = nn.Parameter(
torch.randn(out_channels, in_channels, 1, 1)
)
self.pointwise_bias = nn.Parameter(torch.zeros(out_channels))
self._validated = False
def _naive_forward(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for c in range(self.in_channels):
xc = x[:, c : c + 1]
wc = self.depthwise_weight[c : c + 1]
bc = self.depthwise_bias[c : c + 1]
out_c = F.conv2d(
xc,
wc,
bias=bc,
stride=self.stride,
padding=self.padding,
)
outputs.append(out_c)
depthwise = torch.cat(outputs, dim=1)
outputs = []
for oc in range(self.out_channels):
weight = self.pointwise_weight[oc : oc + 1]
bias = self.pointwise_bias[oc : oc + 1]
acc = torch.zeros(
depthwise.size(0),
1,
depthwise.size(2),
depthwise.size(3),
device=depthwise.device,
dtype=depthwise.dtype,
)
for ic in range(self.in_channels):
acc += F.conv2d(
depthwise[:, ic : ic + 1],
weight[:, ic : ic + 1],
bias=None,
)
acc += bias.view(1, 1, 1, 1)
outputs.append(acc)
pointwise = torch.cat(outputs, dim=1)
return F.relu(pointwise)
def _optimized_forward(self, x: torch.Tensor) -> torch.Tensor:
if x.is_cuda:
torch.backends.cudnn.benchmark = True
x_opt = x.to(memory_format=torch.channels_last) if x.is_cuda else x
depthwise = F.conv2d(
x_opt,
self.depthwise_weight,
bias=self.depthwise_bias,
stride=self.stride,
padding=self.padding,
groups=self.in_channels,
)
pointwise = F.conv2d(
depthwise,
self.pointwise_weight,
bias=self.pointwise_bias,
stride=1,
padding=0,
)
return F.relu(pointwise)
def forward(self, x: torch.Tensor) -> torch.Tensor:
if not self._validated:
y = self._naive_forward(x)
self._validated = True
return y
return self._optimized_forward(x)
def get_inputs():
return [torch.randn(32, 64, 128, 128)]
def get_init_inputs():
return [64, 128, 3, 1, 1, 42]

View File

@ -0,0 +1,84 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
"""
Naive depthwise separable convolution:
- Depthwise 3x3 per channel implemented with explicit Python loops.
- Pointwise 1x1 implemented channel-by-channel.
Very slow but numerically identical to the optimized path.
"""
def __init__(self, in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1, seed=0):
super(Model, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.kernel_size = kernel_size
self.stride = stride
self.padding = padding
if seed is not None:
torch.manual_seed(seed)
self.depthwise_weight = nn.Parameter(
torch.randn(in_channels, 1, kernel_size, kernel_size)
)
self.depthwise_bias = nn.Parameter(torch.zeros(in_channels))
self.pointwise_weight = nn.Parameter(
torch.randn(out_channels, in_channels, 1, 1)
)
self.pointwise_bias = nn.Parameter(torch.zeros(out_channels))
def _depthwise_naive(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for c in range(self.in_channels):
xc = x[:, c : c + 1, :, :]
wc = self.depthwise_weight[c : c + 1]
bc = self.depthwise_bias[c : c + 1]
out_c = F.conv2d(
xc,
wc,
bias=bc,
stride=self.stride,
padding=self.padding,
groups=1,
)
outputs.append(out_c)
return torch.cat(outputs, dim=1)
def _pointwise_naive(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for oc in range(self.out_channels):
weight = self.pointwise_weight[oc : oc + 1]
bias = self.pointwise_bias[oc : oc + 1]
acc = torch.zeros(
x.size(0), 1, x.size(2), x.size(3), device=x.device, dtype=x.dtype
)
for ic in range(self.in_channels):
acc += F.conv2d(
x[:, ic : ic + 1],
weight[:, ic : ic + 1],
bias=None,
stride=1,
padding=0,
)
acc += bias.view(1, 1, 1, 1)
outputs.append(acc)
return torch.cat(outputs, dim=1)
def forward(self, x: torch.Tensor) -> torch.Tensor:
y = self._depthwise_naive(x)
y = self._pointwise_naive(y)
return F.relu(y)
def get_inputs():
return [torch.randn(32, 64, 128, 128)]
def get_init_inputs():
return [64, 128, 3, 1, 1, 42]

43
S1/Ljy123_#10/prompt.txt Normal file
View File

@ -0,0 +1,43 @@
Write a custom CUDA kernel that fuses depthwise convolution, pointwise convolution, batch normalization, and SiLU activation.
The original architecture performs:
1. Depthwise convolution: dw_output = depthwise_conv(input)
2. Pointwise convolution: pw_output = pointwise_conv(dw_output)
3. Batch normalization: bn_output = batch_norm(pw_output)
4. SiLU activation: silu_output = silu(bn_output)
You should fuse these four operations into a single CUDA kernel to avoid:
- Storing intermediate results to global memory
- Multiple memory transfers between operations
The SiLU activation function is defined as:
silu(x) = x * sigmoid(x)
Considerations:
- Use appropriate grid and block dimensions to parallelize over batch size, channels, and spatial dimensions
- Implement efficient shared memory usage for convolution operations
- Handle batch normalization parameters properly
- Ensure numerical stability and precision
You are given the following architecture:
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self, in_channels=64, out_channels=128, kernel_size=3, groups=64):
super(Model, self).__init__()
self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size,
groups=groups, padding=kernel_size//2)
self.pointwise = nn.Conv2d(in_channels, out_channels, 1)
self.bn = nn.BatchNorm2d(out_channels)
def forward(self, x):
# Depthwise convolution
x = self.depthwise(x)
# Pointwise convolution
x = self.pointwise(x)
# Batch normalization
x = self.bn(x)
# SiLU activation
return x * torch.sigmoid(x)

75
S1/Ljy123_#10/run_code.py Normal file
View File

@ -0,0 +1,75 @@
###########################################################
# 性能和精度验证程序
###########################################################
import torch
import torch.nn as nn
import time
from rmsnorm_silu_torchcode import Model, get_inputs, get_init_inputs
from rmsnorm_silu_cudacode import ModelNew
def run_benchmark():
# 检查 CUDA 是否可用
if not torch.cuda.is_available():
print("CUDA 不可用,请确保您有可用的 NVIDIA GPU 并已正确安装 PyTorch CUDA 版本。")
return False, 0.0
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 RMSNorm+SiLU 平均执行时间: {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()