一、GPU调度全景图:什么是GPU任务调度?为什么需要调度?GPU与CPU在调度上的核心差异

1.1 先聊聊“调度”这件事

调度,说白了就是“谁先干、谁后干、干多久”。

你想想看,一个系统里资源就那么多——计算单元、内存带宽、寄存器文件。但任务可能成百上千。谁来决定它们怎么排队?谁来决定哪个任务先拿到计算资源?这就是调度干的事。

我个人习惯把调度比作“交通指挥”。CPU调度像是城市里的红绿灯,车不多,规则简单。GPU调度呢?更像是北京西二旗地铁站早高峰——人山人海,但还得保证每个人都能挤上车,还不能踩踏。

嗯,这个比喻虽然糙了点,但道理是通的。

1.2 为什么需要调度?

你可能觉得:任务来了就执行呗,有什么好调的?

我在项目中遇到过这样的场景:一个深度学习训练任务,把GPU跑满了,但显存利用率只有30%。为什么?因为没有调度好——计算单元在等数据,数据在等搬运,搬运在等总线。说白了,资源在“空转”。

调度要解决的核心问题有三个:

  • 资源利用率:让计算单元、内存、总线别闲着
  • 公平性:多个任务别让一个饿死
  • 延迟控制:实时任务不能等太久

我曾经见过一个团队,写了个CUDA程序,性能死活上不去。后来发现是线程块调度没做好,SM(流多处理器)之间负载严重不均。有的SM忙死,有的SM闲死。这就是调度没搞好的典型后果。

核心观点:没有调度,GPU就是一堆高性能的“废铁”。调度决定了这些算力能不能真正变成性能。

1.3 GPU与CPU在调度上的核心差异

这个问题,我面试过很多人,十个有八个答不全。咱们来拆开看。

1.3.1 调度粒度不同

维度 CPU GPU
调度单位 线程(Thread) 线程束(Warp)/ 线程块(Thread Block)
典型数量 几十个线程 数万个线程
切换开销 高(上下文切换) 极低(硬件零开销切换)

CPU调度一个线程,要保存寄存器、程序计数器、栈指针……一套下来几百个周期。GPU呢?硬件天然支持多线程上下文,切换就是换一组寄存器指针的事。说白了,GPU的线程切换几乎是“免费的”。

1.3.2 调度策略不同

CPU用的是“抢占式调度”。操作系统说了算,时间片到了就给你切走。你正在算一个东西,系统说停,你就得停。这叫“被动让出”。

GPU用的是“协作式调度”。线程块自己决定什么时候让出SM。你算完了,或者卡在访存了,硬件自动切到下一个就绪的线程束。这叫“主动让出”。

为什么会这样?因为GPU的线程太多了。几万个线程,操作系统根本管不过来。只能靠硬件自己搞定。

小技巧:写GPU程序时,尽量让每个线程的工作量差不多。否则会出现“木桶效应”——最慢的那个线程决定了整个线程块的完成时间。我踩过这个坑,后来学乖了。

1.3.3 调度目标不同

CPU调度追求的是“低延迟”。你点一下鼠标,系统得赶紧响应。所以CPU调度器会尽量让交互线程优先执行。

GPU调度追求的是“高吞吐”。你跑一个矩阵乘法,不在乎单个元素算得快不快,在乎的是整个矩阵算完要多快。所以GPU调度器会尽量让所有SM都满载,哪怕单个线程慢一点也没关系。

说白了,CPU是“单兵作战”,GPU是“人海战术”。

1.4 GPU调度全景图

下面这张图,是我自己画的一个GPU调度全景图。你看完应该能对整体有个感觉。

GPU任务调度全景图 CPU 主机端 应用程序 / 驱动 提交命令队列 命令 GPU 全局调度器 GigaThread Engine 分配线程块到SM 管理内存访问 线程块 SM 阵列 SM 0 SM 1 SM 2 SM 3 SM(流多处理器)内部调度 Warp 调度器 每个时钟周期选一个Warp 寄存器文件 256KB / 65536个寄存器 共享内存 48KB ~ 96KB可配 INT32 核心 FP32 核心 Tensor Core LD/ST 单元 调度流程: 1. CPU提交kernel → 2. GigaThread分配线程块到SM → 3. SM内Warp调度器选就绪Warp → 4. 执行单元干活 关键:Warp调度是零开销切换,这是GPU吞吐量的核心保障

这张图里,我刻意把调度分成了三个层次:

  1. 主机端调度:CPU把任务打包成命令,扔给GPU
  2. 全局调度:GigaThread Engine把线程块分配到各个SM
  3. SM内部调度:Warp调度器在每个时钟周期选一个就绪的Warp执行

这三个层次,每一层都有自己的调度策略。你写程序时,其实就是在跟这三层调度打交道。

注意:很多初学者只关注第三层(Warp调度),忽略了前两层。我曾经犯过这个错——在CPU端频繁提交小kernel,结果调度开销占了总时间的一半。后来改成合并提交,性能直接翻倍。

1.5 小结

GPU调度,说白了就是“让几万个线程高效地共享有限的计算资源”。它跟CPU调度最大的区别在于:

  • CPU调度线程,GPU调度Warp
  • CPU是抢占式,GPU是协作式
  • CPU追求低延迟,GPU追求高吞吐

嗯,这些概念先记着。后面我们会一步步深入,把每一层调度都拆开来看。

一句话记住:GPU调度不是“谁先来谁先上”,而是“谁准备好了谁上”。这个思维转变,是理解GPU编程的第一步。

可可精品资料库