Skip to main content

性能瓶颈分析

本文档系统性地介绍 MT GPU 应用程序的性能瓶颈分析方法,涵盖瓶颈识别、诊断技术和优化方案。

note

下文的数字仅做示例参考。具体请以实际情况为准。

瓶颈概述

瓶颈分类

MUSA 应用中的性能瓶颈主要分为四类:

瓶颈类型优化方向
计算瓶颈优化算法、使用 Tensor Core
内存瓶颈优化访问模式、提高带宽利用率
延迟瓶颈增加并行度、提高占用率
同步瓶颈减少同步、异步执行

瓶颈识别流程


分析方法

# 基础分析
mcu -o profile.json ./application

# 指定指标分析
mcu --metrics \
sm_efficiency,\
gld_throughput,\
gst_throughput,\
sm__throughput.avg.pct_of_peak_sustained \
-o profile.json ./application

详细用法: 性能分析工具


计算瓶颈

识别特征

计算瓶颈应用程序的特征:

指标特征
MP 效率高(大于 80%)
内存吞吐量低(小于 50% 峰值)
算术强度
浮点运算吞吐量

诊断代码

// 计算强度分析
void analyzeComputeBound(const void* kernel) {
// 获取设备计算能力
int computeCapability;
musaDeviceGetAttribute(&computeCapability,
musaDevAttrComputeCapabilityMajor, 0);

int registersPerMP;
musaDeviceGetAttribute(&registersPerMP,
musaDevAttrRegistersPerMultiprocessor, 0);

// 理论最大 GFLOPS
float peakGFLOPS = 0.0f;

if (computeCapability 大于等于 8) {
int smCount;
musaDeviceGetAttribute(&smCount,
musaDevAttrMultiProcessorCount, 0);
// FP32: 每个 MP 每周期 256 FLOPs (假设)
peakGFLOPS = smCount * 1.5f * 1000; // 简化计算
}

printf("Peak GFLOPS: %.1f\n", peakGFLOPS);

// 占用率分析
int blockSize = 256;
int maxActiveBlocks;
musaOccupancyMaxActiveBlocks(&maxActiveBlocks, kernel,
blockSize, 0);
printf("Active blocks: %d\n", maxActiveBlocks);
}

优化策略

使用张量核心(Tensor Core):

// 使用 Tensor Core 进行矩阵乘法
#include <musa_tensor.h>

__global__ void tensorCoreGemm(half* a, half* b, float* c,
int M, int N, int K) {
// 使用 warp 矩阵累加(Warp Matrix Multiply Accumulate)
// m, n: 矩阵维度
// k: 累加维度

const int Mtile = 16;
const int Ntile = 16;
const int Ktile = 16;

// 定义矩阵片段
wmma::fragment<wmma::matrix_a, Mtile, Ntile, Ktile, half, wmma::row_major> a_frag;
wmma::fragment<wmma::matrix_b, Mtile, Ntile, Ktile, half, wmma::col_major> b_frag;
wmma::fragment<wmma::accumulator, Mtile, Ntile, Ktile, float> c_frag;

// 初始化
wmma::fill_fragment(c_frag, 0.0f);

// 矩阵乘累加
for (int k = 0; k < K; k += Ktile) {
wmma::load_matrix_sync(a_frag, a + k, K);
wmma::load_matrix_sync(b_frag, b + k * N, N);
wmma::mma_sync(c_frag, a_frag, b_frag, c_frag);
}

// 存储结果
wmma::store_matrix_sync(c, c_frag, N, wmma::mem_row_major);
}

算法优化:

// 优化: 使用查找表代替复杂计算
__device__ float fastLog2(float x) {
// 查找表大小
const int LUT_SIZE = 256;
__shared__ float lut[LUT_SIZE];

int tid = threadIdx.x;
if (tid < LUT_SIZE) {
// 预计算 log2 值
lut[tid] = log2f(1.0f + tid / (float)LUT_SIZE);
}
__syncthreads();

// 使用 LUT
int idx = (int)(x * LUT_SIZE);
idx = min(idx, LUT_SIZE - 1);
return lut[idx];
}

内存瓶颈

识别特征

内存瓶颈应用程序的特征:

指标特征
内存吞吐量接近峰值
MP 效率中等(小于等于 60%)
算术强度低(小于等于 10 FLOPs/Byte)
L2 缓存命中率

诊断代码

void diagnoseMemoryBound() {
// 获取内存带宽
size_t globalMem;
musaDeviceGetAttribute(&globalMem,
musaDevAttrTotalGlobalMem, 0);

int memClockKHz;
musaDeviceGetAttribute(&memClockKHz,
musaDevAttrMemoryClockRate, 0);

int memBusWidth;
musaDeviceGetAttribute(&memBusWidth,
musaDevAttrMemoryBusWidth, 0);

// 理论峰值带宽 (GB/s)
float peakBandwidth = (memClockKHz / 1000.0f) *
(memBusWidth / 8.0f) * 2.0f;

printf("Peak memory bandwidth: %.1f GB/s\n", peakBandwidth);

// 使用 Nsight 测量实际带宽
// ncu --metrics l1tex__t_sectors_pipe_lsu_mem_global_op_ld.sum
}

优化策略

优化访问模式:

// 优化前:内存带宽受限

```c
// 优化前: 内存带宽受限
__global__ void memoryBoundKernel(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;

if (idx < n) {
// 每次只读取 4 字节,计算很少
data[idx] = data[idx] * 0.0f; // 算术强度低
}
}

// 优化后: 增加算术强度
__global__ void computeBoundKernel(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;

if (idx < n) {
float val = data[idx];

// 增加计算密度
#pragma unroll 8
for (int i = 0; i < 8; i++) {
val = val * 1.01f + 0.5f;
val = sinf(val);
}

data[idx] = val;
}
}

使用共享内存:

// 使用共享内存减少全局内存访问
__global__ void sharedMemoryKernel(float* globalData, float* result, int n) {
__shared__ float cache[256];

int tid = threadIdx.x;
int idx = blockIdx.x * blockDim.x + tid;

// 加载数据到共享内存
if (idx < n) {
cache[tid] = globalData[idx];
}
__syncthreads();

// 在共享内存上进行多次计算
float val = cache[tid];
#pragma unroll 4
for (int i = 0; i < 4; i++) {
val += cache[(tid + i) % 256];
}

// 写入结果
if (idx < n) {
result[idx] = val;
}
}

合并访问:

// 优化: 合并内存访问
__global__ void coalescedKernel(float* data, int width, int height) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;

if (x < width && y < height) {
// 合并访问: x 是最内层维度
int idx = y * width + x;
data[idx] = compute(data[idx]);
}
}

延迟瓶颈

识别特征

延迟瓶颈应用程序的特征:

指标特征
每 MP 活跃线程束数
实际占用率低 (小于30%)
分支发散
内存延迟无法隐藏

诊断代码

void diagnoseLatencyBound(const void* kernel) {
int maxThreadsPerMP;
musaDeviceGetAttribute(&maxThreadsPerMP,
musaDevAttrMaxThreadsPerMultiProcessor, 0);

printf("Max threads per MP: %d\n", maxThreadsPerMP);

// 尝试不同的块大小
for (int blockSize = 64; blockSize <= 1024; blockSize += 64) {
int maxActiveBlocks;
musaOccupancyMaxActiveBlocks(&maxActiveBlocks, kernel,
blockSize, 0);

int threadsPerBlock = blockSize;
int activeThreads = maxActiveBlocks * threadsPerBlock;
float occupancy = (float)activeThreads / (float)maxThreadsPerMP;

printf("Block size: %d, Active blocks: %d, Occupancy: %.1f%%\n",
blockSize, maxActiveBlocks, occupancy * 100.0f);
}
}

// Warp 效率分析
void analyzeWarpEfficiency(const void* kernel) {
// 使用 Nsight 分析
// ncu --metrics sm__average_warp_execution_efficiency
}

优化策略

增加并行度:

// 优化: 增加活跃 warp 数量
__global__ void highOccupancyKernel(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;

if (idx < n) {
// 简单计算
data[idx] = data[idx] * 2.0f;
}
}

// 增加线程块数量提高占用率
// 优化配置
int blockSize = 256;
int gridSize = (n + blockSize - 1) / blockSize;

// 动态计算最佳配置
void optimizeGridSize(const void* kernel, int n) {
int maxActiveBlocks;
int blockSize = 256;

musaOccupancyMaxActiveBlocks(&maxActiveBlocks, kernel,
blockSize, 0);

// 使用所有可用块
int mpCount;
musaDeviceGetAttribute(&mpCount,
musaDevAttrMultiProcessorCount, 0);

int totalBlocks = maxActiveBlocks * mpCount;
int optimalGridSize = (n + blockSize - 1) / blockSize;
optimalGridSize = min(optimalGridSize, totalBlocks);

kernel<<<optimalGridSize, blockSize>>>(data, n);
}

减少分支分歧:

// 优化前: 高分支分歧
__global__ void divergentBranch(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;

if (idx < n) {
// 随机分支导致分歧
if (data[idx] > 0.5f) {
data[idx] = sqrtf(data[idx]);
} else {
data[idx] = data[idx] * 2.0f;
}
}
}

// 优化后: 减少分歧
__global__ void nonDivergentBranch(float* data, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;

if (idx < n) {
float val = data[idx];

// 使用掩码操作而不是分支
float result1 = val * 2.0f;
float result2 = sqrtf(val);

// 无分支选择
bool flag = (val > 0.5f);
data[idx] = flag ? result2 : result1;
}
}

同步瓶颈

识别特征

指标特征
流利用率
__syncthreads() 调用频繁
核函数间依赖
GPU-CPU 同步频繁

诊断代码

// 分析同步开销
void analyzeSynchronization() {
musaEvent_t start, stop;
musaEventCreate(&start);
musaEventCreate(&stop);

musaStream_t stream;
musaStreamCreate(&stream);

// 测量核函数时间
musaEventRecord(start, stream);
kernel1<<<grid, block, 0, stream>>>(d_data1);
musaEventRecord(stop, stream);

float kernelTime;
musaEventElapsedTime(&kernelTime, start, stop);

// 测量同步时间
auto startSync = std::chrono::high_resolution_clock::now();
musaStreamSynchronize(stream);
auto endSync = std::chrono::high_resolution_clock::now();

printf("Kernel time: %.3f ms\n", kernelTime);
printf("Synchronization overhead: %.3f ms\n",
std::chrono::duration<float, std::milli>(endSync - startSync).count());
}

优化策略

减少 __syncthreads():

// 优化前: 多次同步
__global__ void excessiveSync(float* data) {
__shared__ float cache[256];

int tid = threadIdx.x;

// 第一阶段
cache[tid] = data[tid];
__syncthreads();
// 计算
if (tid < 128) cache[tid] += cache[tid + 128];
__syncthreads();

// 第二阶段
if (tid < 64) cache[tid] += cache[tid + 64];
__syncthreads();

// 第三阶段
if (tid < 32) cache[tid] += cache[tid + 32];

if (tid == 0) data[0] = cache[0];
}

// 优化后: 减少同步
__global__ void reducedSync(float* data) {
__shared__ float cache[256];

int tid = threadIdx.x;

// 预加载
cache[tid] = data[tid];
__syncthreads();

// 树形归约,只需 log2(256) = 8 次同步
for (int s = 1; s < 256; s *= 2) {
if (tid < 256 / (2 * s))
cache[tid] += cache[tid + s * 128 / 256 * 128 / (2 * s)];
__syncthreads();
}

if (tid == 0) data[0] = cache[0];
}

使用异步操作:

// 异步拷贝和计算重叠
void overlapTransferCompute(float* h_data, float* d_data,
size_t size, musaStream_t stream) {
// 异步拷贝
musaMemcpyAsync(d_data, h_data, size,
musaMemcpyHostToDevice, stream);

// 核函数与拷贝并行
kernel<<<grid, block, 0, stream>>>(d_data);

// 结果回传
musaMemcpyAsync(h_data, d_data, size,
musaMemcpyDeviceToHost, stream);
}

综合分析工具

性能数据收集脚本

#!/bin/bash

# 性能数据收集脚本

echo "=== MUSA Performance Analysis ==="

# 基本信息

echo "Device Info:"
mu-info

# 占用率分析

echo "Occupancy Analysis:"
mcu --metrics sm__achieved_occupancy.pct ./application

# 内存分析

echo "Memory Analysis:"
mcu --metrics gld_throughput,gst_throughput ./application

# 计算分析

echo "Compute Analysis:"
mcu --metrics sm__throughput.avg.pct_of_peak_sustained ./application

# Warp 效率

echo "Warp Efficiency:"
mcu --metrics sm__average_warp_execution_efficiency ./application

优化决策树

决策说明

条件组合瓶颈类型优化方向
MP 效率 > 80%,内存吞吐量 > 80%计算瓶颈优化算法、使用 Tensor Core
MP 效率 > 80%,内存吞吐量 < 80%延迟瓶颈增加指令级并行、减少分支发散
MP 效率 < 80%,内存吞吐量 > 80%内存瓶颈优化访问模式、提高带宽利用率
MP 效率 < 80%,内存吞吐量 < 80%占用率不足增加并行度、优化线程块配置

常见瓶颈案例

案例 1: 矩阵转置

问题分析:

- MP 效率:45%
- 内存读取:80% 峰值
- 内存写入:80% 峰值
- 结论:内存瓶颈(写冲突)

解决方案:

// 使用共享内存优化
__global__ void optimizedTranspose(float* output, float* input,
int width, int height) {
__shared__ float tile[16][16 + 1]; // +1 避免 bank 冲突

int x = blockIdx.x * 16 + threadIdx.x;
int y = blockIdx.y * 16 + threadIdx.y;

// 读取
if (x < width && y < height) {
tile[threadIdx.y][threadIdx.x] = input[y * width + x];
}

__syncthreads();

// 写入
x = blockIdx.y * 16 + threadIdx.x;
y = blockIdx.x * 16 + threadIdx.y;

if (x < height && y < width) {
output[y * height + x] = tile[threadIdx.x][threadIdx.y];
}
}
案例 2: 归约操作

问题分析:

- MP 效率:30%
- 每 MP 活跃线程束:4(共 16 个)
- 结论:延迟瓶颈(占用率低)

解决方案:

// 增加并行度
__global__ void parallelReduction(float* data, float* result, int n) {
__shared__ float cache[256];

int tid = threadIdx.x;
int idx = blockIdx.x * blockDim.x + tid;

// 每个线程处理多个元素
float sum = 0.0f;
for (int i = idx; i < n; i += blockDim.x * gridDim.x) {
sum += data[i];
}
cache[tid] = sum;
__syncthreads();

// 归约
for (int s = blockDim.x / 2; s > 0; s >>= 1) {
if (tid < s) {
cache[tid] += cache[tid + s];
}
__syncthreads();
}

if (tid == 0) {
atomicAdd(result, cache[0]);
}
}

优化效果验证

性能基准测试

void benchmarkOptimization(float* d_data, int n,
void (*kernel)(float*, int),
const char* name) {
musaEvent_t start, stop;
musaEventCreate(&start);
musaEventCreate(&stop);

const int iterations = 100;
float totalTime = 0.0f;

// 预热
for (int i = 0; i < 10; i++) {
kernel<<<gridSize, blockSize>>>(d_data, n);
}
musaDeviceSynchronize();

// 测量
for (int i = 0; i < iterations; i++) {
musaEventRecord(start);
kernel<<<gridSize, blockSize>>>(d_data, n);
musaEventRecord(stop);
musaEventSynchronize(stop);

float ms;
musaEventElapsedTime(&ms, start, stop);
totalTime += ms;
}

printf("%s: %.3f ms (avg of %d runs)\n",
name, totalTime / iterations, iterations);
}

优化前后对比

优化项优化前优化后提升
内存访问45 GB/s380 GB/s8.4x
MP 占用率35%85%2.4x
执行时间12.5 ms2.1 ms6.0x
带宽利用率15%95%6.3x

相关文档