Skip to content

Commit 134c116

Browse files
committed
fix: update version, fix typos, and improve docs
- Fix CMakeLists.txt version: 2.0.0 → 2.1.0 - Fix Chinese typo in RELEASE_NOTES.md: "工你流" → "工作流" - Update _config.yml: add SEO config, social links, better description - Update index.md: add description, forks badge, improve performance table - Update ci.yml: add sm_89, sm_90 architectures, improve info output - Improve benchmark.cuh: add comments for GPU architecture cores - Refactor test_sgemm.cu: use RAII DeviceMemory wrapper instead of raw pointers
1 parent a14f421 commit 134c116

7 files changed

Lines changed: 69 additions & 71 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ jobs:
5151
cmake -S . -B build
5252
-DCMAKE_BUILD_TYPE=Release
5353
-DBUILD_TESTS=OFF
54-
-DCMAKE_CUDA_ARCHITECTURES="70;75;80;86"
54+
-DCMAKE_CUDA_ARCHITECTURES="70;75;80;86;89;90"
5555
5656
- name: Build
5757
run: cmake --build build --target sgemm_benchmark -j$(nproc)
5858

5959
- name: Info
6060
run: |
6161
echo "✅ CUDA compilation successful"
62+
echo "ℹ️ Supported architectures: sm_70, sm_75, sm_80, sm_86, sm_89, sm_90"
6263
echo "ℹ️ GPU runtime tests require a CUDA-capable machine"

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cmake_minimum_required(VERSION 3.18)
22
project(sgemm_optimization
3-
VERSION 2.0.0
3+
VERSION 2.1.0
44
DESCRIPTION "SGEMM optimization from naive to Tensor Core"
55
LANGUAGES CXX CUDA
66
)

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
- 🚀 快速开始指南
3838
- 📖 GPU 架构支持表
3939
- 优化 `_config.yml` Jekyll 配置
40-
- **GitHub Workflows**: 简化并优化工你流
40+
- **GitHub Workflows**: 简化并优化工作流
4141
- `ci.yml`: 清晰的步骤命名
4242
- `pages.yml`: 修复 paths 过滤,改进并发配置
4343

_config.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,38 @@ plugins:
1010
- jekyll-remote-theme
1111
- jekyll-seo-tag
1212
- jekyll-sitemap
13+
- jekyll-default-layout
1314

1415
# Site info
1516
title: SGEMM Optimization
17+
tagline: From Naive to Tensor Core
1618
description: >-
17-
Hand-written, progressively optimized CUDA matrix multiplication — the "Hello World" of HPC.
18-
Five kernel variants from naive triple-loop to Tensor Core WMMA.
19+
Progressive CUDA SGEMM optimization with 5 kernel variants demonstrating
20+
GPU programming techniques including WMMA, shared memory tiling,
21+
bank conflict elimination, and double buffering.
1922
repository: LessUp/sgemm-optimization
23+
author: LessUp
24+
25+
# SEO and Social
26+
social:
27+
name: LessUp
28+
links:
29+
- https://github.com/LessUp
2030

2131
# URLs
2232
url: "https://lessup.github.io"
2333
baseurl: /sgemm-optimization
2434

2535
# Localization
2636
lang: en
27-
author: LessUp
2837

2938
# Navigation
3039
show_downloads: true
40+
github:
41+
is_project_page: true
42+
repository_url: https://github.com/LessUp/sgemm-optimization
43+
zip_url: https://github.com/LessUp/sgemm-optimization/zipball/master
44+
tar_url: https://github.com/LessUp/sgemm-optimization/tarball/master
3145

3246
# Markdown
3347
markdown: kramdown

index.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
layout: default
33
title: SGEMM Optimization
4+
nav_exclude: false
5+
description: Progressive CUDA SGEMM optimization with 5 kernel variants demonstrating GPU programming techniques including WMMA, shared memory tiling, bank conflict elimination, and double buffering.
46
---
57

68
# SGEMM Optimization: From Naive to Tensor Core
@@ -11,6 +13,7 @@ title: SGEMM Optimization
1113
![CUDA](https://img.shields.io/badge/CUDA-11.0+-76B900?logo=nvidia&logoColor=white)
1214
![C++](https://img.shields.io/badge/C%2B%2B-17-00599C?logo=c%2B%2B&logoColor=white)
1315
[![GitHub stars](https://img.shields.io/github/stars/LessUp/sgemm-optimization?style=social)](https://github.com/LessUp/sgemm-optimization/stargazers)
16+
[![GitHub forks](https://img.shields.io/github/forks/LessUp/sgemm-optimization?style=social)](https://github.com/LessUp/sgemm-optimization/network/members)
1417

1518
**Hand-written, progressively optimized CUDA matrix multiplication — the "Hello World" of HPC**
1619

@@ -39,18 +42,18 @@ cmake --build build -j$(nproc)
3942

4043
## 📊 Performance Summary
4144

42-
NVIDIA RTX 3060 Laptop (Ampere, sm_86), 1024×1024×1024:
45+
Tested on NVIDIA RTX 3060 Laptop (Ampere, sm_86), Matrix Size: 1024×1024×1024
4346

4447
| Kernel | GFLOPS | vs cuBLAS | Time | Key Technique |
4548
|--------|-------:|----------:|------:|---------------|
46-
| **cuBLAS** | 5727 | 100% | 0.375 ms | NVIDIA library |
47-
| **Tensor Core** | 2300 | 40.2% | 0.934 ms | WMMA API, FP16 |
48-
| **Tiled** | 753 | 13.1% | 2.853 ms | Shared memory |
49-
| **Double Buffer** | 701 | 12.2% | 3.064 ms | Pipeline overlap |
50-
| **Bank-Free** | 673 | 11.8% | 3.190 ms | Padding |
51-
| **Naive** | 604 | 10.6% | 3.553 ms | Baseline |
52-
53-
> Performance varies by GPU, CUDA version, and matrix size. [See full benchmarks](docs/benchmark-results.md).
49+
| **cuBLAS** | 5727 | 100% | 0.375 ms | NVIDIA optimized library |
50+
| **Tensor Core** | 2300 | 40.2% | 0.934 ms | WMMA API, FP16 compute |
51+
| **Tiled** | 753 | 13.1% | 2.853 ms | Shared memory blocking |
52+
| **Double Buffer** | 701 | 12.2% | 3.064 ms | Latency hiding pipeline |
53+
| **Bank-Free** | 673 | 11.8% | 3.190 ms | Bank conflict elimination |
54+
| **Naive** | 604 | 10.6% | 3.553 ms | Baseline implementation |
55+
56+
> ⚠️ Performance varies by GPU model, CUDA version, and matrix size. [See full benchmark analysis](docs/benchmark-results.md).
5457
5558
---
5659

src/utils/benchmark.cuh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,13 @@ inline float getTheoreticalPeakGflops() {
319319
// See: https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#compute-capabilities
320320
int coresPerSM;
321321
if (prop.major == 7) {
322-
coresPerSM = (prop.minor == 0 || prop.minor == 2) ? 64 : 64; // Volta, Turing
322+
coresPerSM = 64; // Volta (sm_70, sm_72), Turing (sm_75)
323323
} else if (prop.major == 8) {
324-
coresPerSM = (prop.minor == 0 || prop.minor == 6) ? 64 : 128; // Ampere
324+
coresPerSM = (prop.minor == 0 || prop.minor == 6) ? 64 : 128; // A100/sm_80, A10G/sm_86: 64, others: 128
325325
} else if (prop.major == 9) {
326-
coresPerSM = 128; // Hopper
326+
coresPerSM = 128; // Hopper (sm_90), Ada (sm_89 is major=8)
327327
} else {
328-
coresPerSM = 64; // Default fallback
328+
coresPerSM = 64; // Default fallback for older architectures
329329
}
330330

331331
// Use actual GPU clock rate (convert from kHz to GHz)

tests/test_sgemm.cu

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <cuda_runtime.h>
1010
#include <gtest/gtest.h>
11+
#include <memory>
1112
#include <random>
1213
#include <tuple>
1314
#include <vector>
@@ -147,54 +148,42 @@ protected:
147148
initRandomMatrix(h_A_.data(), M_, K_, -1.0f, 1.0f, 42);
148149
initRandomMatrix(h_B_.data(), K_, N_, -1.0f, 1.0f, 123);
149150

150-
CUDA_CHECK(cudaMalloc(&d_A_, M_ * K_ * sizeof(float)));
151-
CUDA_CHECK(cudaMalloc(&d_B_, K_ * N_ * sizeof(float)));
152-
CUDA_CHECK(cudaMalloc(&d_C_, M_ * N_ * sizeof(float)));
153-
CUDA_CHECK(cudaMalloc(&d_C_ref_, M_ * N_ * sizeof(float)));
151+
d_A_ = std::make_unique<DeviceMemory<float>>(M_ * K_);
152+
d_B_ = std::make_unique<DeviceMemory<float>>(K_ * N_);
153+
d_C_ = std::make_unique<DeviceMemory<float>>(M_ * N_);
154+
d_C_ref_ = std::make_unique<DeviceMemory<float>>(M_ * N_);
154155

155-
CUDA_CHECK(cudaMemcpy(d_A_, h_A_.data(), M_ * K_ * sizeof(float), cudaMemcpyHostToDevice));
156-
CUDA_CHECK(cudaMemcpy(d_B_, h_B_.data(), K_ * N_ * sizeof(float), cudaMemcpyHostToDevice));
156+
d_A_->copyFromHost(h_A_.data(), M_ * K_);
157+
d_B_->copyFromHost(h_B_.data(), K_ * N_);
157158

158-
verifier_.computeReference(d_A_, d_B_, d_C_ref_, M_, K_, N_);
159-
CUDA_CHECK(
160-
cudaMemcpy(h_C_ref_.data(), d_C_ref_, M_ * N_ * sizeof(float), cudaMemcpyDeviceToHost));
161-
}
162-
163-
void TearDown() override {
164-
if (d_A_)
165-
cudaFree(d_A_);
166-
if (d_B_)
167-
cudaFree(d_B_);
168-
if (d_C_)
169-
cudaFree(d_C_);
170-
if (d_C_ref_)
171-
cudaFree(d_C_ref_);
159+
verifier_.computeReference(d_A_->get(), d_B_->get(), d_C_ref_->get(), M_, K_, N_);
160+
d_C_ref_->copyToHost(h_C_ref_.data(), M_ * N_);
172161
}
173162

174163
template <typename LaunchFn>
175164
VerifyResult runKernelAndCompare(LaunchFn launch_fn,
176165
VerifyTolerance tolerance = kStandardVerifyTolerance) {
177-
CUDA_CHECK(cudaMemset(d_C_, 0, M_ * N_ * sizeof(float)));
166+
d_C_->zero();
178167
launch_fn();
179168
CUDA_CHECK(cudaDeviceSynchronize());
180-
CUDA_CHECK(cudaMemcpy(h_C_.data(), d_C_, M_ * N_ * sizeof(float), cudaMemcpyDeviceToHost));
169+
d_C_->copyToHost(h_C_.data(), M_ * N_);
181170
return compareMatrices(h_C_.data(), h_C_ref_.data(), M_, N_, tolerance);
182171
}
183172

184173
int M_, K_, N_;
185174
std::vector<float> h_A_, h_B_, h_C_, h_C_ref_;
186-
float *d_A_ = nullptr;
187-
float *d_B_ = nullptr;
188-
float *d_C_ = nullptr;
189-
float *d_C_ref_ = nullptr;
175+
std::unique_ptr<DeviceMemory<float>> d_A_;
176+
std::unique_ptr<DeviceMemory<float>> d_B_;
177+
std::unique_ptr<DeviceMemory<float>> d_C_;
178+
std::unique_ptr<DeviceMemory<float>> d_C_ref_;
190179
SGEMMVerifier verifier_;
191180
};
192181

193182
class NaiveSGEMMTest : public SGEMMKernelTest {};
194183

195184
TEST_P(NaiveSGEMMTest, CorrectnessProperty) {
196185
VerifyResult result =
197-
runKernelAndCompare([&] { launch_naive_sgemm<>(d_A_, d_B_, d_C_, M_, K_, N_); });
186+
runKernelAndCompare([&] { launch_naive_sgemm<>(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); });
198187

199188
EXPECT_TRUE(result.passed) << "Naive SGEMM failed for dimensions " << M_ << "x" << K_ << "x" << N_
200189
<< " (max_rel_error: " << result.max_rel_error << ")";
@@ -207,7 +196,7 @@ class TiledSGEMMTest : public SGEMMKernelTest {};
207196

208197
TEST_P(TiledSGEMMTest, CorrectnessProperty) {
209198
VerifyResult result =
210-
runKernelAndCompare([&] { launch_tiled_sgemm<32>(d_A_, d_B_, d_C_, M_, K_, N_); });
199+
runKernelAndCompare([&] { launch_tiled_sgemm<32>(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); });
211200

212201
EXPECT_TRUE(result.passed) << "Tiled SGEMM failed for dimensions " << M_ << "x" << K_ << "x" << N_
213202
<< " (max_rel_error: " << result.max_rel_error << ")";
@@ -220,7 +209,7 @@ class BankConflictFreeSGEMMTest : public SGEMMKernelTest {};
220209

221210
TEST_P(BankConflictFreeSGEMMTest, CorrectnessProperty) {
222211
VerifyResult result = runKernelAndCompare(
223-
[&] { launch_bank_conflict_free_sgemm<32>(d_A_, d_B_, d_C_, M_, K_, N_); });
212+
[&] { launch_bank_conflict_free_sgemm<32>(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); });
224213

225214
EXPECT_TRUE(result.passed) << "BankConflictFree SGEMM failed for dimensions " << M_ << "x" << K_
226215
<< "x" << N_ << " (max_rel_error: " << result.max_rel_error << ")";
@@ -233,7 +222,7 @@ class DoubleBufferSGEMMTest : public SGEMMKernelTest {};
233222

234223
TEST_P(DoubleBufferSGEMMTest, CorrectnessProperty) {
235224
VerifyResult result =
236-
runKernelAndCompare([&] { launch_double_buffer_sgemm<32>(d_A_, d_B_, d_C_, M_, K_, N_); });
225+
runKernelAndCompare([&] { launch_double_buffer_sgemm<32>(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); });
237226

238227
EXPECT_TRUE(result.passed) << "DoubleBuffer SGEMM failed for dimensions " << M_ << "x" << K_
239228
<< "x" << N_ << " (max_rel_error: " << result.max_rel_error << ")";
@@ -252,7 +241,7 @@ TEST_P(TensorCoreSGEMMTest, FastPathCorrectnessProperty) {
252241
ASSERT_TRUE(tensorCoreDimensionsSupported(M_, K_, N_));
253242

254243
VerifyResult result = runKernelAndCompare(
255-
[&] { launch_tensor_core_sgemm(d_A_, d_B_, d_C_, M_, K_, N_); }, kTensorCoreVerifyTolerance);
244+
[&] { launch_tensor_core_sgemm(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); }, kTensorCoreVerifyTolerance);
256245

257246
EXPECT_TRUE(result.passed) << "TensorCore SGEMM fast path failed for dimensions " << M_ << "x"
258247
<< K_ << "x" << N_ << " (max_rel_error: " << result.max_rel_error
@@ -266,7 +255,7 @@ class TensorCoreFallbackTest : public SGEMMKernelTest {};
266255

267256
TEST_P(TensorCoreFallbackTest, NonAlignedInputsFallbackSafely) {
268257
VerifyResult result = runKernelAndCompare(
269-
[&] { launch_tensor_core_sgemm(d_A_, d_B_, d_C_, M_, K_, N_); }, kStandardVerifyTolerance);
258+
[&] { launch_tensor_core_sgemm(d_A_->get(), d_B_->get(), d_C_->get(), M_, K_, N_); }, kStandardVerifyTolerance);
270259

271260
EXPECT_TRUE(result.passed) << "TensorCore fallback failed for dimensions " << M_ << "x" << K_
272261
<< "x" << N_ << " (max_rel_error: " << result.max_rel_error << ")";
@@ -303,26 +292,22 @@ TEST_F(DimensionInvarianceTest, AllStandardKernelsWorkWithVariousDimensions) {
303292
initRandomMatrix(h_A.data(), M, K, -1.0f, 1.0f, iter);
304293
initRandomMatrix(h_B.data(), K, N, -1.0f, 1.0f, iter + 1000);
305294

306-
float *d_A = nullptr;
307-
float *d_B = nullptr;
308-
float *d_C = nullptr;
309-
float *d_ref = nullptr;
310-
CUDA_CHECK(cudaMalloc(&d_A, M * K * sizeof(float)));
311-
CUDA_CHECK(cudaMalloc(&d_B, K * N * sizeof(float)));
312-
CUDA_CHECK(cudaMalloc(&d_C, M * N * sizeof(float)));
313-
CUDA_CHECK(cudaMalloc(&d_ref, M * N * sizeof(float)));
295+
DeviceMemory<float> d_A(M * K);
296+
DeviceMemory<float> d_B(K * N);
297+
DeviceMemory<float> d_C(M * N);
298+
DeviceMemory<float> d_ref(M * N);
314299

315-
CUDA_CHECK(cudaMemcpy(d_A, h_A.data(), M * K * sizeof(float), cudaMemcpyHostToDevice));
316-
CUDA_CHECK(cudaMemcpy(d_B, h_B.data(), K * N * sizeof(float), cudaMemcpyHostToDevice));
300+
d_A.copyFromHost(h_A.data(), M * K);
301+
d_B.copyFromHost(h_B.data(), K * N);
317302

318-
computeReference(handle_, d_A, d_B, d_ref, M, K, N);
319-
CUDA_CHECK(cudaMemcpy(h_ref.data(), d_ref, M * N * sizeof(float), cudaMemcpyDeviceToHost));
303+
computeReference(handle_, d_A.get(), d_B.get(), d_ref.get(), M, K, N);
304+
d_ref.copyToHost(h_ref.data(), M * N);
320305

321306
auto testKernel = [&](const char *name, auto kernel_func) {
322-
CUDA_CHECK(cudaMemset(d_C, 0, M * N * sizeof(float)));
323-
kernel_func(d_A, d_B, d_C, M, K, N);
307+
d_C.zero();
308+
kernel_func(d_A.get(), d_B.get(), d_C.get(), M, K, N);
324309
CUDA_CHECK(cudaDeviceSynchronize());
325-
CUDA_CHECK(cudaMemcpy(h_C.data(), d_C, M * N * sizeof(float), cudaMemcpyDeviceToHost));
310+
d_C.copyToHost(h_C.data(), M * N);
326311

327312
VerifyResult result =
328313
compareMatrices(h_C.data(), h_ref.data(), M, N, kStandardVerifyTolerance);
@@ -334,11 +319,6 @@ TEST_F(DimensionInvarianceTest, AllStandardKernelsWorkWithVariousDimensions) {
334319
testKernel("Tiled", launch_tiled_sgemm<32>);
335320
testKernel("BankConflictFree", launch_bank_conflict_free_sgemm<32>);
336321
testKernel("DoubleBuffer", launch_double_buffer_sgemm<32>);
337-
338-
cudaFree(d_A);
339-
cudaFree(d_B);
340-
cudaFree(d_C);
341-
cudaFree(d_ref);
342322
}
343323
}
344324

0 commit comments

Comments
 (0)