Skip to content

Commit 041986f

Browse files
committed
refactor: deepen codebase architecture for testability and AI-navigability
## 架构改进 ### 1. 工具层测试隔离 - 新增 tests/test_utils.cu (27 个测试用例) - 测试 DeviceMemory、CublasHandle、SGEMMVerifier 的独立接口 - 覆盖 RAII 语义、移动语义、边界条件、NaN/Inf 处理 ### 2. Benchmark 模块拆分 - 新增 src/utils/benchmark_core.cuh - CUDA 事件计时器 - 新增 src/utils/benchmark_metrics.cuh - 指标计算、理论峰值 - 新增 src/utils/benchmark_cublas.cuh - cuBLAS 参考实现 - 重构 src/utils/benchmark.cuh 为聚合模块 ### 3. 应用层拆分 - 新增 src/cli_parser.cuh - CLI 解析、配置构造 - 新增 src/benchmark_runner.cuh - 内核调度、结果聚合 - 简化 src/main.cu 为入口点组装 (293 行 → 34 行) ### 4. Tensor Core Fallback 策略解耦 - 新增 src/kernels/tensor_core_launcher.cuh - 支持自定义 fallback - 新增 src/kernels/tensor_core_launcher_impl.cuh - 默认实现 - 引入 FallbackKernel 类型,用户可注入自定义策略 ### 5. 性能回归测试框架 - 新增 tests/test_performance.cu (6 个测试用例) - 新增 tests/baselines/ 目录存储基线数据 - 定义每个内核的最小性能阈值 ## 其他更新 - 新增 CONTEXT.md 记录领域模型和架构决策 - 更新 CMakeLists.txt 添加新测试目标 ## 统计 - 测试用例: 11 → 44 (+33) - 测试文件: 1 → 3 (+2)
1 parent ec768b2 commit 041986f

19 files changed

Lines changed: 2354 additions & 693 deletions

CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,34 @@ if(BUILD_TESTS)
7171
$<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr>
7272
)
7373

74+
# 工具层测试
75+
add_executable(test_utils tests/test_utils.cu)
76+
target_include_directories(test_utils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
77+
target_link_libraries(test_utils PRIVATE
78+
GTest::gtest_main
79+
CUDA::cudart
80+
CUDA::cublas
81+
CUDA::curand
82+
)
83+
target_compile_options(test_utils PRIVATE
84+
$<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr>
85+
)
86+
87+
# 性能回归测试
88+
add_executable(test_performance tests/test_performance.cu)
89+
target_include_directories(test_performance PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
90+
target_link_libraries(test_performance PRIVATE
91+
GTest::gtest_main
92+
CUDA::cudart
93+
CUDA::cublas
94+
CUDA::curand
95+
)
96+
target_compile_options(test_performance PRIVATE
97+
$<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr>
98+
)
99+
74100
include(GoogleTest)
75101
gtest_discover_tests(test_sgemm)
102+
gtest_discover_tests(test_utils)
103+
gtest_discover_tests(test_performance)
76104
endif()

CONTEXT.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# SGEMM Optimization 领域模型
2+
3+
本文档定义了项目的核心概念和术语,供 AI 工具和人类开发者参考。
4+
5+
## 核心模块
6+
7+
### Tensor Core 模块
8+
9+
项目将 Tensor Core 功能拆分为三个深度模块,每个模块有独立的职责和测试面:
10+
11+
#### Tensor Core Capabilities
12+
**位置**: `src/kernels/tensor_core_capabilities.cuh`
13+
14+
能力查询接口,提供:
15+
- `tensorCoresAvailable()` - 查询当前设备是否支持 WMMA 操作 (sm_70+)
16+
- `tensorCoreDimensionsSupported(M, K, N)` - 查询给定维度是否适合 Tensor Core 加速
17+
- `getTensorCoreArchName()` - 获取当前设备的 Tensor Core 架构名称
18+
19+
#### Tensor Core Compute
20+
**位置**: `src/kernels/tensor_core_compute.cuh`
21+
22+
纯 WMMA 计算路径,提供:
23+
- `float_to_half_kernel` - FP32 → FP16 转换内核
24+
- `launch_tensor_core_sgemm_fp16()` - 纯 WMMA FP16→FP32 计算入口
25+
- `launch_tensor_core_sgemm_fp16_fast_path()` - 快速路径(无能力检查)
26+
27+
此模块不执行 fallback,用于单独测试 Tensor Core 计算性能。
28+
29+
#### Tensor Core Launcher
30+
**位置**: `src/kernels/tensor_core_launcher.cuh`
31+
32+
统一的 SGEMM 启动接口,提供:
33+
- `launch_tensor_core_sgemm()` - 安全的端到端 FP32 入口点(使用默认 fallback)
34+
- `launch_tensor_core_sgemm_with_fallback()` - 支持自定义 fallback 策略的模板版本
35+
- `FallbackKernel` - fallback 函数类型定义
36+
- `kTensorCoreVerifyTolerance` - Tensor Core 验证容差
37+
38+
此模块处理:
39+
- 设备能力检测
40+
- FP32 → FP16 类型转换
41+
- 不支持情况下的 fallback 到用户指定的策略
42+
43+
**设计优势**
44+
- 解耦 Tensor Core 模块与特定 fallback 内核
45+
- 用户可注入自定义 fallback(如 naive、tiled、bank-conflict-free)
46+
- 支持运行时选择 fallback 策略
47+
48+
#### Tensor Core Launcher Impl
49+
**位置**: `src/kernels/tensor_core_launcher_impl.cuh`
50+
51+
默认实现,使用 bank-conflict-free 作为 fallback。
52+
53+
#### Tensor Core Benchmark
54+
**位置**: `src/kernels/tensor_core_benchmark.cuh`
55+
56+
Tensor Core 特有的 benchmark 功能,提供:
57+
- `runTensorCoreComputeOnlyBenchmark()` - 纯计算路径性能测试
58+
59+
此模块将 Tensor Core 特定逻辑从工具层移到内核层,避免循环依赖。
60+
61+
## 验证模块
62+
63+
**位置**: `src/utils/verify.cuh`
64+
65+
统一的验证逻辑:
66+
- `detail::compareMatricesImpl()` - 内部实现,供其他函数共享
67+
- `compareMatrices()` - 独立的矩阵比较函数
68+
- `SGEMMVerifier` - 带 cuBLAS 句柄的验证器类
69+
70+
## Benchmark 模块
71+
72+
项目将 Benchmark 功能拆分为三个深度模块,每个模块有独立的职责:
73+
74+
### Benchmark Core
75+
**位置**: `src/utils/benchmark_core.cuh`
76+
77+
核心性能测量:
78+
- `CudaTimer` - RAII 包装的 CUDA 事件计时器
79+
- `measureGpuTime()` - 通用的 GPU 操作性能测量器
80+
81+
### Benchmark Metrics
82+
**位置**: `src/utils/benchmark_metrics.cuh`
83+
84+
指标计算:
85+
- `PerformanceMetrics` - 性能指标结构体
86+
- `calculateSgemmMetrics()` - 计算 SGEMM 性能指标
87+
- `getTheoreticalPeakGflops()` / `getTheoreticalPeakBandwidth()` - 理论峰值查询
88+
- `calculateEfficiency()` / `calculateBandwidthUtilization()` - 效率计算
89+
90+
### Benchmark cuBLAS
91+
**位置**: `src/utils/benchmark_cublas.cuh`
92+
93+
cuBLAS 参考实现:
94+
- `CublasSgemm` - cuBLAS SGEMM 参考调用器
95+
- `SgemmReferenceCalculator` - 完整参考计算流程
96+
97+
### 高级接口
98+
**位置**: `src/utils/benchmark.cuh`
99+
100+
聚合模块并提供:
101+
- `SGEMMBenchmark` - 高级 benchmark 编排器
102+
- `BenchmarkResult` - 结果结构和报告生成
103+
104+
## 测试架构
105+
106+
### 测试分层
107+
108+
项目采用分层测试策略,确保每个层级都有独立的测试面:
109+
110+
#### 内核层测试
111+
**位置**: `tests/test_sgemm.cu`
112+
113+
测试内核的正确性:
114+
- 参数化正确性测试(5 个内核 + 多维度组合)
115+
- Tensor Core 快速路径和 fallback 测试
116+
- 边界测试和维度不变性测试
117+
118+
#### 工具层测试
119+
**位置**: `tests/test_utils.cu`
120+
121+
测试工具模块的独立接口:
122+
- `DeviceMemory` - RAII 内存管理、移动语义、边界条件
123+
- `CublasHandle` - cuBLAS 句柄生命周期
124+
- `SGEMMVerifier` - 参考计算和验证逻辑
125+
- `VerifyTolerance` - 容差配置和边界条件
126+
- NaN/Inf 处理、异常安全性
127+
128+
**设计原则**:工具层测试独立于内核测试,可以单独捕获工具类 bug。
129+
130+
#### 性能回归测试
131+
**位置**: `tests/test_performance.cu`
132+
133+
检测性能退化:
134+
- 为每个内核定义最小性能阈值(相对于理论峰值的百分比)
135+
- 测量实际 GFLOPS 并与阈值比较
136+
- 支持基线数据持久化(存储在 `tests/baselines/`
137+
138+
**性能阈值**
139+
- Naive: 5% 峰值
140+
- Tiled: 20% 峰值
141+
- Bank-Conflict-Free: 30% 峰值
142+
- Double-Buffer: 35% 峰值
143+
- Tensor Core: 50% 峰值(当可用时)
144+
145+
**设计原则**:性能测试独立于正确性测试,可在 CI 中检测重大性能退化。
146+
147+
## 架构原则
148+
149+
### 三层架构
150+
151+
1. **应用层** (`main.cu`, `cli_parser.cuh`, `benchmark_runner.cuh`)
152+
- `main.cu` - 入口点,仅负责组装
153+
- `cli_parser.cuh` - 命令行解析、配置构造
154+
- `benchmark_runner.cuh` - 内核调度、结果聚合
155+
2. **内核层** (`src/kernels/`) - 5 个内核实现 + Tensor Core 专用模块
156+
3. **工具层** (`src/utils/`) - RAII 内存管理、错误处理、验证辅助
157+
158+
### 依赖方向
159+
160+
- 应用层 → 内核层 → 工具层
161+
- 内核层可以依赖工具层
162+
- 工具层不应依赖内核层(通过适配器解耦)
163+
164+
### 模块深度原则
165+
166+
- **深层模块**: 小接口,大实现(高杠杆)
167+
- **浅层模块**: 接口复杂度接近实现复杂度(应避免或合并)
168+
169+
## 性能测试维度
170+
171+
| 内核 | 文件 | 优化技术 |
172+
|------|------|----------|
173+
| Naive | `naive_sgemm.cuh` | 基础三重循环,基准实现 |
174+
| Tiled | `tiled_sgemm.cuh` | 共享内存分块,数据复用 |
175+
| Bank-Free | `bank_conflict_free_sgemm.cuh` | 共享内存填充,消除 bank 冲突 |
176+
| Double-Buffer | `double_buffer_sgemm.cuh` | 双缓冲,计算与传输重叠 |
177+
| Tensor Core | `tensor_core_*.cuh` | WMMA API,混合精度 FP16→FP32 |

src/benchmark_runner.cuh

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#pragma once
2+
3+
#include "cli_parser.cuh"
4+
#include "kernels/bank_conflict_free_sgemm.cuh"
5+
#include "kernels/double_buffer_sgemm.cuh"
6+
#include "kernels/naive_sgemm.cuh"
7+
#include "kernels/tensor_core_benchmark.cuh"
8+
#include "kernels/tensor_core_sgemm.cuh"
9+
#include "kernels/tiled_sgemm.cuh"
10+
#include "utils/benchmark.cuh"
11+
#include "utils/cuda_utils.cuh"
12+
#include "utils/verify.cuh"
13+
14+
#include <cstdio>
15+
16+
// ============================================================================
17+
// Benchmark 编排器
18+
// ============================================================================
19+
20+
/**
21+
* SGEMM Benchmark 编排器
22+
*
23+
* 负责调度所有内核 benchmark 并生成报告。
24+
* 与 CLI 解析分离,可被测试或脚本直接调用。
25+
*/
26+
class BenchmarkRunner {
27+
public:
28+
explicit BenchmarkRunner(const BenchmarkConfig& config) : config_(config) {}
29+
30+
/**
31+
* 运行所有配置的 benchmark
32+
*/
33+
void runAll() {
34+
printHeader();
35+
36+
for (const auto& [M, K, N] : config_.dimensions) {
37+
runBenchmarks(M, K, N);
38+
}
39+
40+
printFooter();
41+
}
42+
43+
private:
44+
void printHeader() const {
45+
printf("\n");
46+
printf("====================================================================="
47+
"===========\n");
48+
printf(" SGEMM Optimization Benchmark Suite\n");
49+
printf("====================================================================="
50+
"===========\n");
51+
52+
printGPUInfo();
53+
54+
float peakGflops = getTheoreticalPeakGflops();
55+
float peakBandwidth = getTheoreticalPeakBandwidth();
56+
printf("Approximate theoretical peak FP32: %.2f GFLOPS\n", peakGflops);
57+
printf("Approximate theoretical peak bandwidth: %.2f GB/s\n", peakBandwidth);
58+
printf("\n");
59+
}
60+
61+
void printFooter() const {
62+
printf("\n");
63+
printf("====================================================================="
64+
"===========\n");
65+
printf(" Benchmark Complete\n");
66+
printf("====================================================================="
67+
"===========\n");
68+
printf("\n");
69+
printf("Notes:\n");
70+
printf(" - Standard kernels are verified with shared FP32 tolerances.\n");
71+
printf(" - Tensor Core verification uses relaxed mixed-precision tolerances.\n");
72+
printf(" - The end-to-end Tensor Core result includes FP32->FP16 conversion "
73+
"and safe fallback behavior.\n");
74+
printf(" - The compute-only Tensor Core result is only shown for "
75+
"WMMA-compatible dimensions.\n");
76+
printf("\n");
77+
}
78+
79+
void runBenchmarks(int M, int K, int N) {
80+
printf("\n");
81+
printf("====================================================================="
82+
"===========\n");
83+
printf(" Benchmarking %d x %d x %d SGEMM\n", M, K, N);
84+
printf("====================================================================="
85+
"===========\n");
86+
87+
SGEMMBenchmark benchmark;
88+
89+
// cuBLAS 参考
90+
printf("\nRunning cuBLAS (reference)...\n");
91+
BenchmarkResult cublas_result =
92+
benchmark.runCublas(M, K, N, config_.warmup_runs, config_.benchmark_runs);
93+
float cublas_gflops = cublas_result.gflops;
94+
95+
// 标准内核
96+
runStandardKernels(benchmark, M, K, N);
97+
98+
// Tensor Core 内核
99+
runTensorCoreKernels(benchmark, M, K, N);
100+
101+
// 报告
102+
benchmark.printSummary();
103+
printPerformanceComparison(benchmark.getResults(), cublas_gflops);
104+
105+
// 导出 roofline 数据
106+
char filename[256];
107+
snprintf(filename, sizeof(filename), "roofline_data_%d_%d_%d.csv", M, K, N);
108+
benchmark.exportRooflineData(filename);
109+
}
110+
111+
void runStandardKernels(SGEMMBenchmark& benchmark, int M, int K, int N) {
112+
printf("Running Naive SGEMM...\n");
113+
benchmark.run(
114+
"Naive",
115+
[](const float* A, const float* B, float* C, int M, int K, int N) {
116+
launch_naive_sgemm<32>(A, B, C, M, K, N);
117+
},
118+
M, K, N, config_.warmup_runs, config_.benchmark_runs, kStandardVerifyTolerance);
119+
120+
printf("Running Tiled SGEMM...\n");
121+
benchmark.run(
122+
"Tiled (32x32)",
123+
[](const float* A, const float* B, float* C, int M, int K, int N) {
124+
launch_tiled_sgemm<32>(A, B, C, M, K, N);
125+
},
126+
M, K, N, config_.warmup_runs, config_.benchmark_runs, kStandardVerifyTolerance);
127+
128+
printf("Running Bank Conflict Free SGEMM...\n");
129+
benchmark.run(
130+
"Bank Conflict Free",
131+
[](const float* A, const float* B, float* C, int M, int K, int N) {
132+
launch_bank_conflict_free_sgemm<32>(A, B, C, M, K, N);
133+
},
134+
M, K, N, config_.warmup_runs, config_.benchmark_runs, kStandardVerifyTolerance);
135+
136+
printf("Running Double Buffer SGEMM...\n");
137+
benchmark.run(
138+
"Double Buffer",
139+
[](const float* A, const float* B, float* C, int M, int K, int N) {
140+
launch_double_buffer_sgemm<32>(A, B, C, M, K, N);
141+
},
142+
M, K, N, config_.warmup_runs, config_.benchmark_runs, kStandardVerifyTolerance);
143+
}
144+
145+
void runTensorCoreKernels(SGEMMBenchmark& benchmark, int M, int K, int N) {
146+
if (!tensorCoresAvailable()) {
147+
int device;
148+
CUDA_CHECK(cudaGetDevice(&device));
149+
cudaDeviceProp prop;
150+
CUDA_CHECK(cudaGetDeviceProperties(&prop, device));
151+
printf("Skipping Tensor Core benchmarks (requires sm_70+, current: sm_%d%d)\n",
152+
prop.major, prop.minor);
153+
return;
154+
}
155+
156+
printf("Running Tensor Core SGEMM (end-to-end, includes FP32->FP16 "
157+
"conversion/fallback)...\n");
158+
benchmark.run(
159+
"Tensor Core (WMMA end-to-end)",
160+
[](const float* A, const float* B, float* C, int M, int K, int N) {
161+
launch_tensor_core_sgemm(A, B, C, M, K, N);
162+
},
163+
M, K, N, config_.warmup_runs, config_.benchmark_runs, kTensorCoreVerifyTolerance);
164+
165+
if (tensorCoreDimensionsSupported(M, K, N)) {
166+
printf("Running Tensor Core SGEMM (compute-only WMMA path)...\n");
167+
BenchmarkResult tc_result = runTensorCoreComputeOnlyBenchmark(
168+
benchmark, M, K, N, config_.warmup_runs, config_.benchmark_runs,
169+
kTensorCoreVerifyTolerance);
170+
tc_result.print();
171+
} else {
172+
printf("Skipping Tensor Core compute-only benchmark (requires positive "
173+
"dimensions aligned to 16).\n");
174+
}
175+
}
176+
177+
BenchmarkConfig config_;
178+
};

0 commit comments

Comments
 (0)