在计算机编程领域,优先级是一个无处不在的概念。虽然许多开发者并不经常直接参与优先级相关的编码工作,但在系统设计和任务调度中,优先级的影响随处可见。无论是操作系统中的进程调度、数据结构中的队列处理,还是各类任务排队机制,都离不开优先级的控制逻辑。
简单来说,优先级决定了任务执行的先后顺序。举一个生活中常见的例子:在银行办理业务时,普通客户通常遵循“先到先服务”的原则;但如果有一位VIP客户到来,他可以直接跳过排队人群,优先办理业务。这种机制正是优先级的实际体现。
不仅在CPU任务调度中有优先级,在GPU的任务处理中也同样存在类似的机制。在CUDA编程模型中,流(Stream)作为异步执行的基本单位,也支持优先级设置。当所有流具有相同优先级时,它们会按照提交顺序被调度执行。然而,在某些关键场景下,需要某些任务能够快速响应并抢占资源,这就引入了流的优先级控制功能。
目前主流的GPU架构通常只支持两种优先级级别:高优先级和低优先级。需要注意的是,并非所有GPU硬件都具备优先级调度能力。因此,在使用该功能前,必须首先确认设备是否支持:
可通过CUDA提供的 cudaDeviceGetStreamPriorityRange 接口查询当前设备所支持的优先级范围。若返回的最高优先级值与最低优先级值相等,则说明该设备不支持流优先级功能。
开发者往往希望通过设置高优先级来提升特定任务的执行速度,但实际情况可能并不如预期。优先级仅影响任务被调度的顺序,并不能保证其运行效率或完成时间一定更优。这一点与CPU的线程调度类似。此外,如果优先级配置不当,还可能导致低优先级流长时间得不到执行资源,出现“饥饿”现象。
NVIDIA官方文档指出,未来版本可能会调整与流优先级相关的API接口。因此,在实际开发过程中,建议密切关注最新版CUDA Toolkit的文档说明,以确保代码的兼容性和稳定性。
设置流优先级的需求源于现实应用场景中对实时性的要求。例如,在人工智能广泛应用的今天,自动驾驶系统需要在检测到行人突然闯入时立即触发紧急制动指令;又或者在大规模数据分析中,临时需要提取敏感或关键信息。这些场景都需要部分计算任务获得更高的调度权重,从而实现快速响应。
[此处为图片1]以下是一个来自NVIDIA官方的标准方式设置流优先级的代码片段:
// 获取当前设备支持的流优先级范围
int leastPriority, greatestPriority;
cudaDeviceGetStreamPriorityRange(&leastPriority, &greatestPriority);
// 创建具有最高和最低优先级的流
cudaStream_t st_high, st_low;
cudaStreamCreateWithPriority(&st_high, cudaStreamNonBlocking, greatestPriority);
cudaStreamCreateWithPriority(&st_low, cudaStreamNonBlocking, leastPriority);
上述代码首先调用接口获取设备支持的优先级区间,然后根据所得范围创建不同优先级的CUDA流,以便后续任务分配使用。
下面展示一个简单的CUDA程序示例,演示如何利用不同优先级的流来执行向量加法和矩阵乘法任务:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <cstdio>
__global__ void vecAdd(const float* a, const float* b, float* c, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
for (int i = 0; i < 100; i++) {
c[idx] = a[idx] + b[idx];
}
}
}
__global__ void matrixMul(const float* a, const float* b, float* c, int n) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < n && col < n) {
float sum = 0.0f;
此例中定义了两个核函数:一个用于执行向量加法,另一个用于矩阵乘法运算。通过将它们分别提交至不同优先级的流中,可以模拟高优先任务抢占资源的行为,进而观察调度行为的变化。
for (int k = 0; k < n; k++) { for (int i = 0; i < 10; i++) { sum += a[row * n + k] * b[k * n + col]; } } c[row * n + col] = sum; } int main() { printf("*************start CUDA Priority demo****************\n"); int devID = 0; cudaSetDevice(devID); cudaDeviceProp prop; cudaGetDeviceProperties(&prop, devID); printf("device name: %s\n", prop.name); printf("CUDA Capability: %d.%d\n", prop.major, prop.minor); int lowPriority, highPriority; cudaDeviceGetStreamPriorityRange(&lowPriority, &highPriority); printf("PriorityRange: least=%d, greattest=%d\n", lowPriority, highPriority); if (lowPriority == highPriority) { printf("not supprot!\n"); return 0; } cudaStream_t highPriorityStream, normalPriorityStream, lowPriorityStream; // 创建高优先级流 cudaStreamCreateWithPriority(&highPriorityStream, cudaStreamNonBlocking, highPriority); // 创建中等优先级流 int normalPriority = (lowPriority + highPriority) / 2; cudaStreamCreateWithPriority(&normalPriorityStream, cudaStreamNonBlocking, normalPriority); // 创建低优先级流 cudaStreamCreateWithPriority(&lowPriorityStream, cudaStreamNonBlocking, lowPriority); printf("create three priority type stream:\n"); printf(" *** high (priority=%d)\n", highPriority); printf(" *** normal (priority=%d)\n", normalPriority); printf(" *** low (priority=%d)\n", lowPriority); const int vecSize = 1 << 20; const int matrixSize = 512; const size_t vecBytes = vecSize * sizeof(float); const size_t matrixBytes = matrixSize * matrixSize * sizeof(float); float *dA, *dB, *dC; float *matrixA, *matrixB, *matrixC; cudaMalloc(&dA, vecBytes); cudaMalloc(&dB, vecBytes); cudaMalloc(&dC, vecBytes); cudaMalloc(&matrixA, matrixBytes); cudaMalloc(&matrixB, matrixBytes); cudaMalloc(&matrixC, matrixBytes); cudaEvent_t startHigh, stopHigh; cudaEvent_t startNormal, stopNormal; cudaEvent_t startLow, stopLow; cudaEventCreate(&startHigh); cudaEventCreate(&stopHigh); cudaEventCreate(&startNormal); cudaEventCreate(&stopNormal); cudaEventCreate(&startLow);初始化事件并创建低优先级流的停止事件:
cudaEventCreate(&stopLow);
输出提示信息,表示即将启动不同优先级的内核任务:
printf("\n streams:Start the kernel, different priorities...\n");
定义向量操作的线程块和网格尺寸:
dim3 vecBlockDim(256);
dim3 vecGridDim((vecSize + vecBlockDim.x - 1) / vecBlockDim.x);
定义矩阵运算的线程块与网格尺寸:
dim3 matBlockDim(16, 16);
dim3 gridDim_mat((matrixSize + matBlockDim.x - 1) / matBlockDim.x, (matrixSize + matBlockDim.y - 1) / matBlockDim.y);
[此处为图片1]在各自流中记录开始事件:
cudaEventRecord(startHigh, highPriorityStream);
cudaEventRecord(startNormal, normalPriorityStream);
cudaEventRecord(startLow, lowPriorityStream);
提示高优先级加法任务开始:
printf("high start add...\n");
执行向量加法内核,使用高优先级流:
vecAdd << <vecGridDim, vecBlockDim, 0, highPriorityStream >> >(dA, dB, dC, vecSize);
检查是否出现CUDA错误:
cudaGetLastError();
提示普通优先级乘法任务启动:
printf("normal start mul...\n");
执行矩阵乘法,运行在普通优先级流中:
matrixMul << <gridDim_mat, matBlockDim, 0, normalPriorityStream >> >(matrixA, matrixB, matrixC, matrixSize);
检测执行过程中的错误:
cudaGetLastError();
提示低优先级乘法任务启动:
printf("low start mul other...\n");
在低优先级流中执行另一组矩阵乘法:
matrixMul << <gridDim_mat, matBlockDim, 0, lowPriorityStream >> >(matrixA, matrixB, matrixC, matrixSize);
确认无运行时异常:
cudaGetLastError();
[此处为图片2]在各流中记录对应任务结束的时间点:
cudaEventRecord(stopHigh, highPriorityStream);
cudaEventRecord(stopNormal, normalPriorityStream);
cudaEventRecord(stopLow, lowPriorityStream);
输出等待所有任务完成的提示信息:
printf("\n waitting end...\n");
同步三个流,确保所有任务执行完毕:
cudaStreamSynchronize(highPriorityStream);
cudaStreamSynchronize(normalPriorityStream);
cudaStreamSynchronize(lowPriorityStream);
声明用于存储耗时的变量:
float msHigh, msNormal, msLow;
计算各个任务的实际执行时间(单位:毫秒):
cudaEventElapsedTime(&msHigh, startHigh, stopHigh);
cudaEventElapsedTime(&msNormal, startNormal, stopNormal);
cudaEventElapsedTime(&msLow, startLow, stopLow);
打印性能统计标题:
printf("\n******** exec time calculate ********\n");
分别输出高、普通、低优先级任务所消耗的时间:
printf("high task Time consumed by priority: %.2f ms\n", msHigh);
printf("normal task Time consumed by priority: %.2f ms\n", msNormal);
printf("low task Time consumed by priority: %.2f ms\n", msLow);
输出关于任务执行顺序的说明:
printf("\n********execution order************\n");
printf("Tasks in high priority streams usually start executing earlier\n");
最后释放所有已创建的事件资源:
cudaEventDestroy(startHigh);
cudaEventDestroy(stopHigh);
cudaEventDestroy(startNormal);
cudaEventDestroy(stopNormal);
cudaEventDestroy(startLow);
return 0;
cudaEventDestroy(stopLow);
cudaStreamDestroy(highPriorityStream);
cudaStreamDestroy(normalPriorityStream);
cudaStreamDestroy(lowPriorityStream);
cudaFree(dA);
cudaFree(dB);
cudaFree(dC);
cudaFree(matrixA);
cudaFree(matrixB);
cudaFree(matrixC);
上述代码将事件机制与流的优先级控制进行了结合使用,不仅回顾了CUDA事件的基本操作方式,也直观展示了如何在实际中应用不同优先级的流。[此处为图片1]
在技术学习过程中,基础知识常常给人一种“一看就懂”的错觉,导致很多人对其重视不足。这种现象类似于中学阶段的学习:看书时觉得内容简单明了,但一旦合上书本尝试独立解题,却往往无从下手。这并非说明基础不重要,而是反映出学习者并未真正掌握和理解这些基本概念。
只有通过持续的实践与反复的巩固,才能将零散的知识点内化为扎实的能力。唯有打好根基,后续的技术进阶才有可能实现灵活运用与创新突破。
扫码加好友,拉您进群



收藏
