算子开发知识整理

引言

在CUDA并行编程中,Reduce(归约)和SGEMM(单精度矩阵乘法)是两种基础且关键的运算模式,其性能优化直接影响GPU计算效率。本文系统梳理了Reduce操作从基础到高级的优化技术路线(v1-v8),并简要介绍SGEMM优化思路,旨在帮助开发者深入理解GPU内存架构、线程调度机制,掌握性能瓶颈分析与优化方法,从而在实际项目中实现高效的GPU计算加速。

GPU内存架构图

避免线程束分化

Reduce版本优化

v1

关键优化点:使用共享内存(shared memory)减少全局内存访问次数,提升访存效率。

cpp 复制代码
__global__ void reduce_v1(float *d_in, float *d_out) {
    extern __shared__ float sdata[];
    
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    
    // 从全局内存加载数据到共享内存
    sdata[tid] = d_in[i];
    __syncthreads();
    
    // 在共享内存中进行归约
    for (unsigned int s = 1; s < blockDim.x; s *= 2) {
        if (tid % (2 * s) == 0) {
            sdata[tid] += sdata[tid + s];
        }
        __syncthreads();
    }
    
    // 将结果写回全局内存
    if (tid == 0) {
        d_out[blockIdx.x] = sdata[0];
    }
}
v2

关键优化点:消除线程束分化(warp divergence),通过改变循环步长避免条件分支,提高warp执行效率。

cpp 复制代码
__global__ void reduce_v2(float *d_in, float *d_out) {
    extern __shared__ float sdata[];
    
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    
    sdata[tid] = d_in[i];
    __syncthreads();
    
    // 优化:消除warp divergence
    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
        if (tid < s) {
            sdata[tid] += sdata[tid + s];
        }
        __syncthreads();
    }
    
    if (tid == 0) {
        d_out[blockIdx.x] = sdata[0];
    }
}
v3

关键优化点:消除bank conflict,通过调整共享内存访问模式避免多个线程同时访问同一bank。

cpp 复制代码
__global__ void reduce_v3(float *d_in, float *d_out) {
    extern __shared__ float sdata[];
    
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    
    sdata[tid] = d_in[i];
    __syncthreads();
    
    // 优化:消除bank conflict
    for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) {
        if (tid < s) {
            sdata[tid] += sdata[tid + s];
        }
        __syncthreads();
    }
    
    // 处理最后32个元素(一个warp)
    if (tid < 32) {
        // 使用warp shuffle或直接计算
        sdata[tid] += sdata[tid + 32];
        sdata[tid] += sdata[tid + 16];
        sdata[tid] += sdata[tid + 8];
        sdata[tid] += sdata[tid + 4];
        sdata[tid] += sdata[tid + 2];
        sdata[tid] += sdata[tid + 1];
    }
    
    if (tid == 0) {
        d_out[blockIdx.x] = sdata[0];
    }
}
v4.1,主要用来解决idle线程

关键优化点:减少block数量,保持每个block中线程数不变,让每个线程处理更多数据,提高线程利用率。

cpp 复制代码
__global__ void reduce_v4_1(float *d_in, float *d_out, int n) {
    extern __shared__ float sdata[];
    
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x * (blockDim.x * 2) + threadIdx.x;
    unsigned int gridSize = blockDim.x * 2 * gridDim.x;
    
    // 每个线程处理多个元素
    float sum = 0;
    while (i < n) {
        sum += d_in[i];
        if (i + blockDim.x < n) {
            sum += d_in[i + blockDim.x];
        }
        i += gridSize;
    }
    
    sdata[tid] = sum;
    __syncthreads();
    
    // 归约(使用v2/v3的优化)
    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
        if (tid < s) {
            sdata[tid] += sdata[tid + s];
        }
        __syncthreads();
    }
    
    if (tid == 0) {
        d_out[blockIdx.x] = sdata[0];
    }
}
v4.2,主要用来解决idle线程

关键优化点:保持block数量不变,减少block中idle线程数量,调整线程分配策略。

cpp 复制代码
__global__ void reduce_v4_2(float *d_in, float *d_out, int n) {
    extern __shared__ float sdata[];
    
    unsigned int tid = threadIdx.x;
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    unsigned int stride = blockDim.x * gridDim.x;
    
    // 每个线程处理多个元素,减少idle线程
    float sum = 0;
    while (i < n) {
        sum += d_in[i];
        i += stride;
    }
    
    sdata[tid] = sum;
    __syncthreads();
    
    // 归约
    for (unsigned int s = blockDim.x / 2; s > 0; s >>= 1) {
        if (tid < s) {
            sdata[tid] += sdata[tid + s];
        }
        __syncthreads();
    }
    
    if (tid == 0) {
        d_out[blockIdx.x] = sdata[0];
    }
}
v5,unroll last warp
  1. __syncthreads()是用来同步不同warp的,但是到最后小于一个warp数量的时候可能会出现warp divergence,那怎么办呢?直接一个warp内的线程做一样的操作就好了
  2. volatile关键字
v6. completely unroll

就是不用循环,直接展开

v7. 设置block的合理数量
  1. 一个thread累加多次,让block不要空闲,可以更好地隐藏延迟
v8. shuffle
  1. 通过__shfl_down_sync()进行reduce,避免走shared memory,加速

SGEMM优化

Reference

  1. CUDA优化基础
  2. B站CUDA优化视频
  3. CUDA_QuanShuang GitHub
  4. CUDA性能优化
  5. NVIDIA GPU性能优化基础
  6. CUDA Tutorial
  7. 算子手撕
  8. 面试经验1
  9. B站视频
  10. 深入浅出GPU优化
相关推荐
派勤电子10 天前
AI 加速卡与工控机集成优化 2026 软硬件协同实操指南
边缘计算·机器视觉·工控机·ai推理·工业主机·ai工控机·ai工业应用
叶子Talk23 天前
OpenAI破解80年数学猜想,AI首次做出原创证明
人工智能·数学·算法·机器学习·ai·openai·ai推理
苏渡苇1 个月前
DeepSeek V4 实战:自然语言生成 SQL + 智能优化引擎
ai·springboot·spring ai·deepseek·ai推理·deepseek v4·自然语言生成sql
李大锤同学2 个月前
Qwen3.5-4B-Claude-Opus部署教程:GPU显存监控与llama.cpp参数调优
大语言模型·ai推理·gpu优化
tiger1192 个月前
FPGA独立实现LLM推理方案——FlighLLM
fpga开发·llm·fpga·ai推理
DO_Community2 个月前
DigitalOcean 收购 Katanemo Labs:迎接 Agent 时代,重塑基础设施
人工智能·ai推理
DO_Community3 个月前
教程:让OpenClaw一次接入Claude、Qwen、DeepSeek 多个模型
人工智能·aigc·ai编程·ai推理
DO_Community3 个月前
如何使用DigitalOcean Gradient 平台上的无服务器推理
人工智能·aigc·ai编程·ai推理
DO_Community3 个月前
使用 DigitalOcean 实现 Claude Code “低配订阅 + 外部 Token”
人工智能·aigc·ai编程·ai推理