1.3 流处理器(SM/SMX)架构详解 摘要 流处理器(Streaming Multiprocessor,简称SM)是NVIDIA GPU的核心计算单元,是SIMT执行模型的硬件载体。本文深入剖析SM架构的设计原理、内部结构、工作原理及其演进历程,揭示现代GPU如何通过精心设计的SM架构实现大规模并行计算的极致性能。从最初的SM架构到现代的SMX(Streaming Multiprocessor with X),我们将详细分析SM的设计哲学、硬件组成、资源管理机制以及性能优化策略,为理解GPU并行计算提供深入的技术洞察。 SM架构基础 1.1 SM的定义与发展历程 1.1.
流处理器(Streaming Multiprocessor,简称SM)是NVIDIA GPU的核心计算单元,是SIMT执行模型的硬件载体。本文深入剖析SM架构的设计原理、内部结构、工作原理及其演进历程,揭示现代GPU如何通过精心设计的SM架构实现大规模并行计算的极致性能。从最初的SM架构到现代的SMX(Streaming Multiprocessor with X),我们将详细分析SM的设计哲学、硬件组成、资源管理机制以及性能优化策略,为理解GPU并行计算提供深入的技术洞察。
SM的定义:
SM是NVIDIA GPU中的基本计算单元,是一个独立的并行处理器,包含多个CUDA核心和丰富的硬件资源。SM负责执行线程块(Thread Block),是GPU并行计算的基本执行单元。
SM的核心作用:
SM在GPU中的位置:
GPU ├── GPU Chip │ ├── SM 0 │ ├── SM 1 │ ├── SM 2 │ │ ├── CUDA Cores │ │ ├── Warp Schedulers │ │ ├── Register File │ │ ├── Shared Memory │ │ └── L1 Cache │ ├── SM 3 │ └── ... ├── Memory Subsystem │ ├── L2 Cache │ ├── Memory Controller │ └── Memory Interfaces └── Interconnect ├── Crossbar Switch └── NVLink
第一代SM架构(Tesla架构):
第二代SM架构(Fermi架构):
第三代SM架构(Kepler架构):
第四代SM架构(Maxwell架构):
第五代SM架构(Pascal架构):
第六代SM架构(Volta架构):
第七代SM架构(Turing架构):
第八代SM架构(Ampere架构):
第九代SM架构(Hopper架构):
第十代SM架构(Blackwell架构):
SM的整体结构:
SM (Streaming Multiprocessor) ├── Warp Scheduler Units (2-4) │ ├── Warp Scheduler 0 │ ├── Warp Scheduler 1 │ ├── Warp Scheduler 2 │ └── Warp Scheduler 3 ├── CUDA Cores (64-128) │ ├── FP32 Unit │ ├── FP64 Unit │ ├── INT32 Unit │ └── Special Function Unit (SFU) ├── Tensor Cores (0-8) │ ├── Matrix Multiply Unit │ ├── Activation Unit │ └── Accumulation Unit ├── RT Core (0-1) │ ├── Ray Tracing Unit │ ├── Bounding Volume Hierarchy (BVH) Unit │ └── Shadow Unit ├── Register File (64K-128K registers) ├── Shared Memory / L1 Cache (64KB-128KB) ├── Constant Cache (64KB) ├── Texture Cache (128KB) └── Memory Interface ├── Load/Store Units └── Address Generation Units
核心组件详解:
Warp调度器(Warp Scheduler):
CUDA核心:
Tensor Core:
RT Core:
寄存器文件(Register File):
共享内存/缓存:
寄存器分配:
共享内存分配:
执行单元分配:
SM的初始化:
1. GPU启动 ├── 检测GPU硬件配置 ├── 初始化SM资源 ├── 加载固件 └── 准备执行环境 2. SM激活 ├── 分配SM资源 ├── 初始化Warp调度器 ├── 准备寄存器文件 └── 加载共享内存 3. 内核启动 ├── 分配Thread Block ├── 分配Warp ├── 加载指令缓存 └── 开始执行
Thread Block的分配:
Warp的创建:
指令流水线:
取指 → 译码 → 调度 → 发射 → 执行 → 写回
详细执行步骤:
取指(Fetch):
译码(Decode):
调度(Schedule):
发射(Issue):
执行(Execute):
写回(Writeback):
双发射执行:
在Ampere架构中,SM支持双发射执行:
周期1: - 发射指令A到执行单元0 - 发射指令B到执行单元1 周期2: - 发射指令C到执行单元0 - 发射指令D到执行单元1
线程状态转换:
Active → Waiting → Finished → Exited ↓ ↓ ↓ ↓ Ready Memory Sync Complete Sync Barrier Block Resource
线程状态详解:
活跃(Active):
等待(Waiting):
完成(Finished):
退出(Exited):
线程掩码管理:
SMX的定义:
SMX(Streaming Multiprocessor with X)是NVIDIA在Pascal架构中引入的增强型SM架构,相比之前的SM架构,SMX在性能和效率方面有显著提升。
SMX的核心特点:
SMX的设计哲学:
硬件对比:
| 组件 | SM (Kepler) | SMX (Pascal) | 改进比例 |
|---|---|---|---|
| CUDA核心 | 192 | 128 | -33% |
| Warp调度器 | 4 | 4 | 0% |
| 寄存器文件 | 64K | 64K | 0% |
| 共享内存 | 64KB | 64KB-128KB | 100% |
| Tensor Core | 0 | 8 (Volta+) | 新增 |
| RT Core | 0 | 1 (Turing+) | 新增 |
性能对比:
| 性能指标 | SM (Kepler) | SMX (Pascal) | 改进比例 |
|---|---|---|---|
| FP32性能 | 1.3 TFLOPS | 1.3 TFLOPS | 0% |
| INT8性能 | 5.2 TOPS | 5.2 TOPS | 0% |
| 内存带宽 | 320 GB/s | 900 GB/s | 181% |
| 功耗 | 250W | 300W | 20% |
| 能效比 | 5.2 TFLOPS/W | 4.3 TFLOPS/W | -17% |
架构对比:
SMX的整体结构:
SMX (Streaming Multiprocessor with X) ├── Warp Scheduler Units (4) │ ├── Dual-Issue Scheduler │ ├── Instruction Cache │ └── Resource Management ├── CUDA Cores (128) │ ├── FP32 Units │ ├── FP64 Units │ ├── INT32 Units │ └── SFU Units ├── Specialized Units │ ├── Tensor Cores (8, Volta+) │ ├── RT Cores (1, Turing+) │ └── Double Precision Units ├── Memory Subsystem │ ├── Register File (128K registers) │ ├── Shared Memory / L1 Cache (128KB) │ ├── Constant Cache (64KB) │ └── Texture Cache (128KB) └── Interconnect ├── Crossbar Switch └── Network Interface
Warp调度器增强:
CUDA核心增强:
专用加速器:
寄存器文件管理:
共享内存管理:
执行单元管理:
Warp调度算法:
1. 获取就绪Warp列表 2. 根据优先级选择Warp 3. 检查资源可用性 4. 分配执行单元 5. 发射指令 6. 更新Warp状态
优先级策略:
资源检查:
指令流水线优化:
内存访问优化:
计算优化:
第一代到第二代:
第二代到第三代:
第三代到第四代:
第四代到第五代:
第五代到第六代:
第六代到第七代:
第七代到第八代:
第八代到第九代:
内存带宽演进:
缓存层次演进:
CUDA版本演进:
编程接口演进:
核心数量的优化:
执行效率的提升:
资源利用率的提升:
内存带宽的提升:
缓存系统的优化:
内存访问模式的优化:
Tensor Core的引入:
RT Core的引入:
双精度单元的增强:
寄存器配置优化:
// 优化前:过多的寄存器使用 __global__ void kernel_with_many_registers(float* data, int n) { float local_data[100]; // 使用过多寄存器 for (int i = 0; i < n; i++) { local_data[i % 100] = data[i] * 2.0f; } } // 优化后:减少寄存器使用 __global__ void optimized_kernel(float* data, int n) { float temp; // 使用少量寄存器 for (int i = 0; i < n; i++) { temp = data[i] * 2.0f; data[i] = temp; } }
共享内存配置优化:
// 优化前:固定共享内存大小 __global__ void fixed_shared_memory_kernel(float* data, int n) { __shared__ float shared[64]; // 固定大小 for (int i = 0; i < 64; i++) { shared[i] = data[i]; } __syncthreads(); for (int i = 0; i < 64; i++) { data[i] = shared[i] * 2.0f; } } // 优化后:动态共享内存配置 __global__ void optimized_shared_memory_kernel(float* data, int n) { extern __shared__ float* shared; // 动态配置 int tid = threadIdx.x; shared[tid] = data[tid]; __syncthreads(); data[tid] = shared[tid] * 2.0f; }
CUDA核心优化:
// 优化前:不充分利用CUDA核心 __global__ void inefficient_kernel(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { data[tid] = sqrtf(data[tid]); // 单一操作 } } // 优化后:充分利用CUDA核心 __global__ void optimized_kernel(float* data1, float* data2, float* data3, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { // 多个并行操作 data1[tid] = sqrtf(data1[tid]); data2[tid] = powf(data2[tid], 2.0f); data3[tid] = expf(data3[tid]); } }
Tensor Core优化:
// 优化前:使用基本矩阵乘法 __global__ void naive_matmul(float* A, float* B, float* C, int M, int N, int K) { int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x; float sum = 0.0f; for (int k = 0; k < K; k++) { sum += A[row * K + k] * B[k * N + col]; } C[row * N + col] = sum; } // 优化后:使用Tensor Core __global__ void tensor_core_matmul(float* A, float* B, float* C, int M, int N, int K) { extern __shared__ float shared_mem[2 * 16 * 16]; float* tile_A = shared_mem; float* tile_B = shared_mem + 16 * 16; int row = blockIdx.y * 16 + threadIdx.y; int col = blockIdx.x * 16 + threadIdx.x; float sum = 0.0f; for (int k = 0; k < K; k += 16) { // 加载tile到共享内存 for (int i = 0; i < 16; i++) { if (row < M && k + i < K) { tile_A[threadIdx.y * 16 + i] = A[row * K + k + i]; } } for (int j = 0; j < 16; j++) { if (col < N && k + j < K) { tile_B[threadIdx.x * 16 + j] = B[(k + j) * N + col]; } } __syncthreads(); // 使用Tensor Core进行矩阵乘法 for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { sum += tile_A[threadIdx.y * 16 + i] * tile_B[j * 16 + threadIdx.x]; } } __syncthreads(); } if (row < M && col < N) { C[row * N + col] = sum; } }
合并访问优化:
// 优化前:不合并的内存访问 __global__ void uncoalesced_access(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { data[tid * 2] = some_calculation(tid); data[tid * 2 + 1] = some_calculation(tid + 1); } } // 优化后:合并的内存访问 __global__ void coalesced_access(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { data[tid] = some_calculation(tid); } }
缓存优化:
// 优化前:直接访问全局内存 __global__ void naive_cache_access(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { float sum = 0.0f; for (int i = 0; i < n; i++) { sum += data[i]; // 频繁访问全局内存 } data[tid] = sum; } } // 优化后:使用缓存和共享内存 __global__ void optimized_cache_access(float* data, int n) { extern __shared__ float shared_mem[256]; int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { // 加载数据到共享内存 for (int i = 0; i < 256; i++) { shared_mem[i] = data[tid * 256 + i]; } __syncthreads(); // 使用共享内存计算 float sum = 0.0f; for (int i = 0; i < 256; i++) { sum += shared_mem[i]; } data[tid] = sum; } }
块大小优化:
// 优化前:不合适的块大小 dim3 block(16, 16); // 256个线程 dim3 grid((n + 255) / 256, (m + 255) / 256); // 优化后:合适的块大小 dim3 block(32, 8); // 256个线程,更好的内存访问模式 dim3 grid((n + 255) / 256, (m + 255) / 256);
线程束优化:
// 优化前:避免Warp分歧 __global__ void divergent_kernel(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { if (data[tid] > 0.0f) { data[tid] = sqrtf(data[tid]); // 分歧 } else { data[tid] = data[tid] * 2.0f; // 分歧 } } } // 优化后:避免Warp分歧 __global__ void optimized_kernel(float* data, int n) { int tid = blockIdx.x * blockDim.x + threadIdx.x; if (tid < n) { unsigned int mask = __ballot_sync(0xFFFFFFFF, data[tid] > 0.0f); if (mask) { // 处理正数 int idx = __ffs_sync(mask) - 1; data[tid] = sqrtf(data[tid]); } if (~mask) { // 处理非正数 int idx = __ffs_sync(~mask) - 1; data[tid] = data[tid] * 2.0f; } } }
同步优化:
// 优化前:频繁同步 __global__ void