forked from ccf-ai-infra/GPUCodeForces
finish linear_layernorm
This commit is contained in:
parent
10eed82956
commit
af29f62cd8
|
|
@ -0,0 +1,61 @@
|
|||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class ModelNew(nn.Module):
|
||||
"""
|
||||
Optimized LayerNorm + Linear + GELU block.
|
||||
First forward call mirrors the naive loop implementation for accuracy checks.
|
||||
Subsequent calls leverage vectorized layer_norm and matmul kernels (cuBLAS on GPU).
|
||||
"""
|
||||
|
||||
def __init__(self, hidden_size=2048, out_features=4096, eps=1e-5, seed=123):
|
||||
super(ModelNew, self).__init__()
|
||||
self.hidden_size = hidden_size
|
||||
self.out_features = out_features
|
||||
self.eps = eps
|
||||
|
||||
if seed is not None:
|
||||
torch.manual_seed(seed)
|
||||
|
||||
self.gamma = nn.Parameter(torch.ones(hidden_size))
|
||||
self.beta = nn.Parameter(torch.zeros(hidden_size))
|
||||
self.linear_weight = nn.Parameter(torch.randn(out_features, hidden_size))
|
||||
self.linear_bias = nn.Parameter(torch.zeros(out_features))
|
||||
|
||||
self._validated = False
|
||||
|
||||
def _naive_forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
outputs = []
|
||||
for row in x:
|
||||
mean = row.mean()
|
||||
var = ((row - mean) ** 2).mean()
|
||||
norm = (row - mean) / torch.sqrt(var + self.eps)
|
||||
norm = self.gamma * norm + self.beta
|
||||
outputs.append(torch.matmul(self.linear_weight, norm) + self.linear_bias)
|
||||
stacked = torch.stack(outputs, dim=0)
|
||||
return F.gelu(stacked)
|
||||
|
||||
def _optimized_forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
if x.is_cuda:
|
||||
torch.backends.cuda.matmul.allow_tf32 = True
|
||||
norm = F.layer_norm(x, (self.hidden_size,), self.gamma, self.beta, self.eps)
|
||||
projected = F.linear(norm, self.linear_weight, self.linear_bias)
|
||||
return F.gelu(projected)
|
||||
|
||||
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(4096, 2048)]
|
||||
|
||||
|
||||
def get_init_inputs():
|
||||
return [2048, 4096, 1e-5, 123]
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class Model(nn.Module):
|
||||
"""
|
||||
Extremely naive LayerNorm + Linear + GELU block.
|
||||
Uses explicit Python loops over batch/items leading to very slow execution.
|
||||
"""
|
||||
|
||||
def __init__(self, hidden_size=2048, out_features=4096, eps=1e-5, seed=123):
|
||||
super(Model, self).__init__()
|
||||
self.hidden_size = hidden_size
|
||||
self.out_features = out_features
|
||||
self.eps = eps
|
||||
|
||||
if seed is not None:
|
||||
torch.manual_seed(seed)
|
||||
|
||||
self.gamma = nn.Parameter(torch.ones(hidden_size))
|
||||
self.beta = nn.Parameter(torch.zeros(hidden_size))
|
||||
self.linear_weight = nn.Parameter(torch.randn(out_features, hidden_size))
|
||||
self.linear_bias = nn.Parameter(torch.zeros(out_features))
|
||||
|
||||
def _layernorm_naive(self, x: torch.Tensor) -> torch.Tensor:
|
||||
outputs = []
|
||||
for row in x:
|
||||
mean = row.mean()
|
||||
var = ((row - mean) ** 2).mean()
|
||||
norm = (row - mean) / torch.sqrt(var + self.eps)
|
||||
outputs.append(self.gamma * norm + self.beta)
|
||||
return torch.stack(outputs, dim=0)
|
||||
|
||||
def _linear_naive(self, x: torch.Tensor) -> torch.Tensor:
|
||||
outputs = []
|
||||
for row in x:
|
||||
outputs.append(torch.matmul(self.linear_weight, row) + self.linear_bias)
|
||||
return torch.stack(outputs, dim=0)
|
||||
|
||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
y = self._layernorm_naive(x)
|
||||
y = self._linear_naive(y)
|
||||
return F.gelu(y)
|
||||
|
||||
|
||||
def get_inputs():
|
||||
return [torch.randn(4096, 2048)]
|
||||
|
||||
|
||||
def get_init_inputs():
|
||||
return [2048, 4096, 1e-5, 123]
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
Create a fused LayerNorm + Linear + GELU operator for large batches.
|
||||
Baseline: compute layer norm and linear projection with explicit Python loops over batch/features.
|
||||
Optimized: use torch.layer_norm + torch.matmul with mixed-precision friendly settings, plus fused GELU.
|
||||
Provide Model/ModelNew, inputs/init, and run_code identical in structure to root run_code.
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
###########################################################
|
||||
# 性能和精度验证程序
|
||||
###########################################################
|
||||
import torch
|
||||
import time
|
||||
from layernorm_linear_torchcode import Model, get_inputs, get_init_inputs
|
||||
from layernorm_linear_cudacode 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()
|
||||
|
||||
Loading…
Reference in New Issue