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的硬件调度器对优先级的支持比较弱。所以,优先级的效果是硬件相关的。
4.6 实际应用场景
我总结几个常见的用法:
| 场景 | 推荐优先级 | 原因 |
|---|---|---|
| 实时渲染 | 高 | 必须保证帧率稳定 |
| 数据预处理 | 低 | 可以慢慢来,不着急 |
| 用户交互响应 | 高 | 不能让用户等 |
| 后台日志分析 | 低 | 不影响主流程就行 |
说白了,优先级就是给你一个"软控制"的手段。它不像硬件中断那么强硬,但用好了,确实能提升系统的响应性。
最后提醒一句:不要滥用高优先级。如果所有流都是高优先级,那就没有高优先级了。合理分配,才能物尽其用。