1. GPU调度概述

各位同学,咱们今天聊聊GPU调度。说实话,我做了十几年GPU系统架构,见过太多因为调度不当导致性能翻车的案例。这章算是整个课程的基石,把这里搞明白了,后面那些花里胡哨的优化技巧才能用对地方。

1.1 GPU到底是怎么工作的?

先说说GPU的核心思想。说白了,GPU就是一台「暴力计算」机器。CPU是精明的管家,一个核心能处理各种复杂任务;GPU则是流水线上的工人,成千上万个核心干同一件简单的事。

我习惯把GPU比作一个大型工厂:

  • SM(流式多处理器) —— 工厂里的车间
  • Warp(线程束) —— 车间里的工作组,通常是32个工人一组
  • Thread(线程) —— 每个工人

嗯,这里有个关键点:GPU不是每个线程独立运行的。它是「单指令多线程」(SIMT)模式。什么意思?就是32个线程(一个warp)同时执行同一条指令。如果线程之间分支不同,那就惨了——部分线程要等待,这就是所谓的「warp divergence」。

核心概念:GPU的并行度来自大量线程的并发执行,而不是单个线程的执行速度。你想想看,一个GPU可能有上万个线程在跑,但每个线程其实跑得很慢。

1.2 调度问题是怎么来的?

问题来了:这么多线程,谁先跑?谁后跑?跑多久?

我在项目中遇到过这样一个场景:一个深度学习训练任务,GPU利用率只有30%。查了半天,发现是调度器把计算密集型任务和内存密集型任务混在一起,导致计算单元经常空转等数据。

调度问题的根源其实就三个:

  1. 资源竞争 —— 寄存器、共享内存、计算单元都是有限的
  2. 延迟隐藏 —— 访存延迟动辄几百个周期,得靠调度来「填坑」
  3. 负载不均 —— 有的线程算得快,有的算得慢,快的得等慢的

说白了,调度就是解决「谁该干活」的问题。但GPU的调度和CPU完全不同。CPU调度是操作系统层面的,时间片轮转;GPU调度是硬件层面的,以warp为单位,而且切换开销几乎为零。

一个小技巧:GPU调度器会维护一个「就绪warp池」。当一个warp因为访存被阻塞时,调度器立刻切换到另一个就绪的warp。这就是为什么我们需要大量线程——线程越多,调度器越容易找到「替补」。

1.3 调度优化的目标是什么?

目标其实很明确:最大化硬件利用率。但怎么衡量?我一般看三个指标:

指标 含义 理想值
Occupancy(占用率) 活跃warp数与最大warp数的比值 ≥ 75%
利用率 计算单元实际工作时间占比 ≥ 80%
延迟隐藏率 访存延迟中被其他计算覆盖的比例 ≥ 90%

你可能会问:占用率越高越好吗?不一定。我踩过这个坑——为了追求高占用率,给每个线程分配很少的寄存器,结果导致寄存器溢出,性能反而下降。所以优化是个平衡的艺术。

1.4 挑战在哪里?

说实话,调度优化最大的挑战是「看不见」。GPU内部是个黑盒,你很难知道调度器到底在干什么。我曾经为了定位一个调度问题,硬是写了几个微基准测试来反推调度器的行为。

常见的挑战包括:

  • warp调度策略不透明 —— 不同代GPU的调度策略不一样,Turing和Ampere就有差异
  • 资源分配与性能的权衡 —— 多给线程分配寄存器能提高单线程性能,但会降低占用率
  • 内存访问模式的影响 —— 非合并访问会导致调度器频繁等待内存
  • 同步开销 —— __syncthreads用不好,整个block都得等

避坑指南:我曾经在一个项目中,为了优化一个kernel,把block大小从256改到512。结果性能反而下降了30%。原因是寄存器压力太大,导致每个SM能同时运行的block数从4个降到了2个。所以,别盲目改参数,要理解背后的资源约束。

1.5 本章知识体系

下面这张图是我自己画的,把GPU调度的核心逻辑串起来了。你看一遍应该能有个整体印象:

GPU调度核心知识体系 GPU Kernel 启动 硬件Warp调度器 以warp为单位,零开销切换 资源分配 寄存器 / 共享内存 / 线程数 决定Occupancy 延迟隐藏 访存 vs 计算 warp切换策略 负载均衡 warp divergence block分配策略 最大化硬件利用率 高性能 / 低延迟 / 高吞吐

这张图把整个调度优化的脉络理清了。从kernel启动开始,经过硬件调度器,再到资源分配、延迟隐藏、负载均衡这三个核心环节,最终目标是最大化硬件利用率。后面每一章都会围绕这张图展开。

总结一下:GPU调度不是玄学,它是有规律可循的。理解硬件的工作原理,知道调度器在想什么,再配合合适的资源分配策略,你就能把GPU的潜力榨干。我见过太多人一上来就调block size、调grid size,结果调了半天性能没变化——因为他们根本不知道调度器在背后做了什么。

好了,这一章就到这里。记住一句话:调度优化的本质,是让GPU的每一个计算单元都忙起来,别闲着。