4、流优先级:创建具有不同优先级的流、优先级对调度的影响

好,咱们接着聊流的话题。

前面我们讲的流,都是"平级"的。但在实际项目中,你会发现有些任务必须赶紧跑,有些任务可以慢慢来。这时候,流的优先级就派上用场了。

4.1 为什么需要流优先级?

我举个例子。我在做实时视频处理时,遇到过这么个场景:

  • 一个流负责处理视频帧,必须实时输出,否则画面会卡顿
  • 另一个流负责后台模型更新,慢一点没关系

如果两个流优先级一样,GPU可能会"公平"地分配资源。结果呢?视频帧被模型更新任务打断了,画面开始掉帧。

你想想看,这多尴尬。

所以,CUDA从某个版本开始(我记得是CUDA 5.0之后),引入了流优先级机制。说白了,就是让高优先级的流能"插队"。

4.2 创建具有优先级的流

创建优先级流,用的是 cudaStreamCreateWithPriority 这个API。它的签名长这样:

cudaError_t cudaStreamCreateWithPriority(
    cudaStream_t* pStream,      // 输出:创建的流
    unsigned int flags,         // 通常传 cudaStreamNonBlocking 或 0
    int priority                // 优先级值,数值越小优先级越高
);

这里有个坑,我一开始就踩过——优先级值的范围是设备相关的。你得先查一下:

int leastPriority, greatestPriority;
cudaDeviceGetStreamPriorityRange(&leastPriority, &greatestPriority);
// leastPriority 是数值最大的(优先级最低)
// greatestPriority 是数值最小的(优先级最高)

嗯,这里要注意:greatestPriority 这个名字有点反直觉。它代表的是"最高优先级",但数值上反而是最小的(通常是负数)。

来个完整的例子:

cudaStream_t highStream, lowStream;

// 获取优先级范围
int low, high;
cudaDeviceGetStreamPriorityRange(&low, &high);
// 假设 low = 0, high = -5

// 创建高优先级流(数值小)
cudaStreamCreateWithPriority(&highStream, cudaStreamNonBlocking, high);
// 创建低优先级流(数值大)
cudaStreamCreateWithPriority(&lowStream, cudaStreamNonBlocking, low);

// 使用
myKernel<<<grid, block, 0, highStream>>>(...);  // 这个先跑
myKernel<<<grid, block, 0, lowStream>>>(...);   // 这个后跑

// 清理
cudaStreamDestroy(highStream);
cudaStreamDestroy(lowStream);

4.3 优先级对调度的影响

优先级到底怎么影响调度?我用自己的理解给你讲清楚。

GPU的硬件调度器,说白了就是个"看人下菜碟"的家伙。当多个流都有就绪的任务时,它会优先选择高优先级的流来发射指令。

具体来说:

  • 高优先级流:它的任务会优先被调度到SM上执行
  • 低优先级流:只有高优先级流没有就绪任务时,才会轮到它

但这里有个关键点——优先级影响的是"调度顺序",而不是"抢占"

什么意思?

如果一个低优先级的kernel已经在SM上跑了,高优先级的任务不会把它踢下来。高优先级任务只能"等"下一个调度机会。所以,如果你想让高优先级任务立刻执行,最好确保SM有空闲。

我曾经在项目中犯过这个错。以为设了高优先级就能"抢跑",结果发现低优先级的kernel占着SM不放,高优先级的任务还是得排队。嗯,后来我调整了策略,把低优先级任务的block数设小一点,留出SM给高优先级任务用。

4.4 优先级与默认流的关系

默认流(NULL流)的优先级是多少?

答案是:默认优先级。它介于最高和最低之间。

所以,如果你创建了一个高优先级流,它的任务会比默认流先执行。反之,低优先级流会比默认流后执行。

我个人的习惯是:

  • 对延迟敏感的任务(如交互式渲染)用高优先级流
  • 对吞吐量敏感的任务(如批量数据处理)用低优先级流
  • 一般任务就用默认流,省事

4.5 避坑指南

注意:优先级不是万能的!
  • 优先级只影响"调度顺序",不影响"执行速度"
  • 如果GPU资源已经占满,优先级再高也得等
  • 不同GPU型号支持的优先级范围可能不同,务必先查询

我曾经遇到过一个诡异的问题:在Titan V上设了高优先级流,效果很明显。但换到GTX 1080上,几乎没区别。后来一查,原来1080的硬件调度器对优先级的支持比较弱。所以,优先级的效果是硬件相关的

小技巧:如果你不确定优先级是否生效,可以用NVIDIA Nsight或nvprof来观察流的调度情况。看高优先级流的kernel是不是更早被发射。

4.6 实际应用场景

我总结几个常见的用法:

场景 推荐优先级 原因
实时渲染 必须保证帧率稳定
数据预处理 可以慢慢来,不着急
用户交互响应 不能让用户等
后台日志分析 不影响主流程就行

说白了,优先级就是给你一个"软控制"的手段。它不像硬件中断那么强硬,但用好了,确实能提升系统的响应性。

最后提醒一句:不要滥用高优先级。如果所有流都是高优先级,那就没有高优先级了。合理分配,才能物尽其用。