掘金 人工智能 05月23日 10:13
为什么说Softmax是访存密集型算子?
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

文章深入探讨了Softmax算子的访存特性,揭示其性能瓶颈在于内存带宽而非算力。通过分析Softmax的计算流程,对比计算密集型算子,文章阐明了Softmax的访存密集本质,并提供了Kernel Fusion、warp-level优化等方法,旨在减少访存次数,提高GPU上的数据读写效率。文章还通过实例和数据分析,详细阐述了优化策略及其效果,强调了在GPU上优化Softmax时,访存效率的重要性。

🧠 Softmax 运算流程涉及最大值计算、指数运算及归一化,这些步骤需要频繁访问内存,读写输入数据和中间值,导致其成为访存密集型算子。

💡 与计算密集型算子(如矩阵乘法)相比,Softmax的计算量相对较低,但数据传输量大,内存带宽成为其性能瓶颈。这导致Softmax的FLOP/Byte比值较低,表明其受限于内存带宽。

🚀 优化Softmax的关键在于减少访存次数,例如通过Kernel Fusion将多个步骤合并为一个Kernel,从而减少内存访问和Kernel启动开销。此外,在GPU上使用warp-level的高带宽读写,可以提高访存效率。

🛠️ Kernel Fusion通过合并计算步骤,减少了内存的load/store操作,多个kernel启动开销,并使用register/shared memory保持局部性。而warp-level优化则侧重于warp内线程的内存访问对齐,从而避免分散访问,提高访存效率。

Softmax 被称为访存密集型算子(memory-bound operator),原因如下:

1.softmax计算流程

以最常见的按行 softmax(如在 NLP 中的 attention)为例,它的计算主要包含以下几步:

    最大值计算(为了数值稳定性):

    指数运算并求和

    归一化

2.为什么访存密集(Memory-bound)

这些操作共同特点是:

特性原因
频繁访问内存多次读写输入 x,保存中间值(如 m_i, s_i, exp(x_{ij})
计算量低于数据传输量每个元素最多几次浮点操作(加法、指数、除法),但必须完整读写内存
缺乏重用性中间结果不能长期缓存,数据局部性差
缓存不友好行维度可能较短,容易导致 cache miss,特别在 batch 较小时

结果是:计算不复杂,但大量的数据移动消耗了带宽,这使得 softmax 性能受限于内存带宽,而不是算力。

3.对比计算密集型算子

类型示例特点
计算密集型矩阵乘法、卷积每读取一次数据,可重复使用多次进行运算(高 FLOPs / Byte)
访存密集型Softmax、LayerNorm每读取的数据只用一次,FLOPs / Byte 比低

4.举个例子

对于一个二维矩阵输入 X ∈ [B, N](batch size 为 B,每行 N 个元素),典型 softmax 实现分为三步:

4.1 Softmax 计算步骤

4.2 定量分析:访存量 vs 运算量

假设输入:

2.计算量(FLOPs)

3. FLOP / Byte Ratio

结果:

这个比值很低(相比典型的计算密集型操作如 matmul,FLOP/Byte 往往能到 5~20),说明这是内存受限的操作

4.3 对比:矩阵乘法(MatMul)FLOP/Byte

比如做:

这是计算密集型操作的典型特征。

4.4 总结

操作类型FLOP / Byte(越大越计算密集)访存瓶颈?
Softmax≈ 0.25✅ 是
矩阵乘法≈ 167❌ 否

因此,Softmax 是访存密集型的(memory-bound),主要瓶颈在内存带宽,不是计算性能

3.优化启示

3.1 fused kernel

3.1.1 在 GPU 或 HPC 系统上,有两个关键挑战:

❶ 内存访问次数多、读写分散:

这种高频访问 global memory 会浪费大量带宽。

❷ 每个阶段独立 Kernel 调用 → 调度 & latency 浪费

3.1.2 解决方案:Kernel Fusion

核心思想:将多个 softmax 步骤合并为一个 kernel,减少 memory 访问和 kernel 启动开销

3.1.3 例子:Fused Softmax kernel 包含以下逻辑

global void fused_softmax(float* x, float* out, int N) {// 使用 shared memory 缓存一行shared float row[1024];

int tid = threadIdx.x;int bid = blockIdx.x;// Step 1: 加载、找最大值float max_val = -INFINITY;for (int i = tid; i < N; i += blockDim.x) {    float val = x[bid * N + i];    row[i] = val;    max_val = max(max_val, val);}__syncthreads();// Step 2: 计算 exp 和 sumfloat sum = 0.0f;for (int i = tid; i < N; i += blockDim.x) {    row[i] = expf(row[i] - max_val);    sum += row[i];}__syncthreads();// Step 3: 写回归一化值for (int i = tid; i < N; i += blockDim.x) {    out[bid * N + i] = row[i] / sum;}

}

🎯 优化效果

优化方式说明效果
Kernel Fusion减少 kernel 调用、内存读写次数运行时间减少 30%~50%
Shared Memory 缓存减少 global memory 带宽压力进一步提升效率
Vectorized Load每次加载多个元素(如 float4提高吞吐
Warp-wise Reduce使用 warp shuffle 优化 max/sum减少同步开销

3.1.4 案例:深度学习框架中对 softmax 的优化

框架优化版本使用的技术
TensorFlowtf.nn.softmaxXLA JIT 编译 + kernel fusion
PyTorchtorch.nn.functional.softmaxFused kernel in CUDA
ONNXonnxruntime + kernel fusionTVM / triton fused kernels

3.1.5 总结:为什么要做 kernel fusion for softmax?

问题Kernel Fusion 如何缓解
多次访问内存开销大合并计算步骤,减少 load/store
多个 kernel 启动开销大一个 kernel 完成所有步骤
多个中间变量占用 cache用 register/shared memory 保持局部性
总体算力利用率低提升 GPU occupancy,降低 latency

3.2 warp-level 的高带宽读写

在 GPU 上优化 softmax 时,核心关注点是如何高效地进行数据的读写(特别是 warp-level 的内存访问),而不是提升计算单元的利用率(算力/FLOPs)

3.2.1 Warp-level 优化是关键

Warp = 一组 32 个线程组成一个 GPU 最小调度单元

GPU 的访存效率依赖于 warp 中线程的 内存访问是否对齐(coalesced access):

在 softmax 中:

3.2.2 举例说明:

比如你要对一个矩阵 [batch_size=128, dim=96] 做 softmax:

这种方式比用多个 kernel、反复访存更高效。

3.2.3 总结

传统优化重点Softmax 优化重点
提升算力利用率提升访存效率(coalesced memory access)
增加并行线程提高 warp 内内存对齐、高带宽读写
更大 tile/block更高效地加载/缓存数据(如 shared memory)

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Softmax 访存密集 Kernel Fusion GPU优化
相关文章