在大规模深度学习训练与推理场景中,GPU 计算框架的性能瓶颈往往不来自于算力本身,而在于内核调度效率与数据传输开销。NVIDIA CUDA 生态经过十余年演进,形成了以内核融合(Kernel Fusion)与流调度(Stream Scheduling)为核心的性能优化范式。本文系统梳理这两种技术的工程实践要点,为构建高效 GPU 流水线提供可操作的参数指引。

内核融合的本质与性能收益

内核融合的本质是将多个细粒度计算操作合并为单一内核,从而在三个维度产生收益。首先是降低启动开销 —— 每次 CUDA 内核_launch 都涉及 CPU 端的参数准备与 GPU 端的资源分配,单次小内核的启动延迟可达数微秒至数十微秒级别,融合后可将此类开销摊销至整体计算中。其次是减少全局内存流量 —— 融合前,中间结果需要写回全局内存再被下一内核读取,融合后这些中间数据可以保留在寄存器或共享内存中,显著提升算术强度。最后是改善 SM 占用率 —— 更大的融合内核可以调度更多线程块,使 GPU 调度器有更充裕的 warp 资源来隐藏内存访问延迟。

垂直融合(Vertical Fusion)是最常见的融合模式,适用于存在生产者 - 消费者依赖的操作序列。例如将 GEMM、偏置加法、激活函数、Dropout 融合为单一内核,可以在同一个数据分块上完成全部计算,避免中间结果的全局内存读写。水平融合(Horizontal Fusion)则用于合并多个独立的逐元素操作 —— 当这些操作在不同的数据区域上执行时,可以将它们合并为带有分支判断的内核,由每个线程根据标识执行对应计算。图表级别融合(Graph-level Fusion)由深度学习编译器(如 XLA、TensorRT、TVM、PyTorch 2.x 的 torch.compile)自动完成,通过分析计算图识别可融合子图,生成优化的融合内核。

融合决策需要权衡收益与资源约束。激进的融合可能导致寄存器溢出或共享内存不足,反而降低 occupancy(SM 上活跃的线程块数量)。工程实践中通常设置资源上限:单线程寄存器数量不超过 256 个,共享内存每块不超过 48KB,并在此约束下决定是否进行融合。

CUDA 流调度与并发策略

CUDA 流(Stream)提供了内核执行的偏序关系控制 —— 同一流内的内核严格串行执行,不同流的内核可以在资源允许范围内并发执行。然而,GPU 的并发能力存在硬件限制:单 SM 上同时驻留的线程块数量有上限(通常为 16 至 32 个,取决于计算能力),单个流内的计算密集型内核往往已经占满 SM 资源,留给其他流的并发空间有限。

高效的流调度策略遵循两条原则。其一是将真正独立的计算管道分配到不同流 —— 例如多路视频解码、多模型并行推理等场景,不同流的 workload 在 SM 资源上自然错开。其二是利用 CUDA 事件(Event)实现精确的流间同步,避免不必要的全局 Barrier 导致的 stall。对于需要严格依赖的场景,优先考虑在内核内部通过同步块(__syncthreads)完成,而非依赖流的隐式顺序。

CUDA Graphs 是近年来最重要的调度优化工具。它将整个计算迭代(图神经网络的一次前向 + 反向传播、推理 pipeline 的一个阶段)捕获为静态图,后续迭代只需重放该图,避免了重复的内核解析与调度开销。CUDA Graphs 与内核融合形成互补 —— 编译器先完成操作融合生成少数大型内核,运行时再将这些内核记录为 Graph,实现端到端的低开销执行。

主流框架的融合实现参考

TensorRT 采用层级化融合策略,将卷积、批归一化、激活融合为单一 CBR(Convolution-BatchNorm-ReLU)内核,将多个卷积层串联为单一 Long Kernel,将 Reshape、Transpose 等布局变换操作也纳入融合范围。开发者可通过 TensorRT Profiler 观察融合效果 —— 若在 Nsight Systems 中看到大量短内核队列或内核间明显间隙,往往意味着融合不足或调度不当。

PyTorch 2.x 的 torch.compile 基于 Triton 编译器实现自动内核融合。与传统 CUDA C 编程不同,Triton 允许使用 Python 语法编写融合内核,由编译器自动处理共享内存分配与线程块调度。对于逐元素操作链(如多个 activation 组合),Triton 往往能生成比手写 CUDA 更优的融合代码。实际调优时可通过 torch._dynamo.config.suppress_errors = True 与 TORCH_COMPILE_DEBUG 观察融合后的内核命名与执行时间。

TVM 采用成本模型驱动的融合决策,通过对目标硬件的微基准测试构建性能模型,自动决定哪些操作应该融合、哪些应该分离。对于算子融合后的代码,TVM 还会进行内存访问模式优化 —— 将数据分块(tiling)到共享内存并采用双缓冲(double buffering)隐藏内存读取延迟。

可落地的工程参数清单

以下是面向 H100/A100 系列 GPU 的推荐工程参数,可作为初始配置基线:内核融合层面,建议将单内核的 grid size 设置为 SM 总数的 2 至 4 倍(确保满载),单线程 registers 上限设为 64(可通过 --maxrregcount 编译选项控制),共享内存使用量控制在每块 32KB 以内以保留 occupancy 空间。流调度层面,建议将独立计算任务分配至 4 至 8 个不同流以实现宏观并发,使用 CUDA Event 标记关键同步点并通过 cudaEventElapsedTime 测量实际执行间隔。Graph 优化层面,对于形状固定的推理任务,优先使用 CUDA Graphs 捕获并重放,建议针对常见 batch size 分别构建专用 Graph 以避免形状适配开销。监控层面,通过 Nsight Systems 分析 GPU 执行时间线,关注内核间隔(kernel interval)与实际计算时间之比,理想情况下该比值应低于 5%。

需要注意的是,上述参数需要根据具体 workload 进行 profile-guided 调优。计算密集型模型(如 Transformer)往往受益于激进的融合与更大的内核尺寸,而内存密集型模型(如部分 CNN 变体)则需要更精细的 occupancy 控制以增加并行内存请求。


资料来源

  • NVIDIA Developer Blog: Advanced CUDA Kernel Optimization Techniques
  • CUDA Kernel Fusion Strategies - Emergent Mind
  • NVIDIA GTC 2025: Ways to Write CUDA Kernels (Session S72449)