如何理解CUDA Stream?

本文介绍了CUDA编程中的Stream概念,包括其在并发控制、数据传输管理和优化中的作用,以及如何创建、使用和销毁CUDAStream和CUDAEvents。通过实例展示了如何通过Stream实现计算和数据传输的异步处理以提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何理解CUDA Stream?

参考

CUDA随笔之Stream的使用 - 知乎 (zhihu.com)

CUDA ---- Stream and Event - 苹果妖 - 博客园 (cnblogs.com)

背景

CUDA程序并发性可分为两种:

1、kernel level concurrency:一个task由GPU上多个thread并行执行的。(我们前几张介绍的都是kernel level的开发)

2、grid level level concurrency:多个task在GPU上同时执行。

(一切为了速度,冲冲冲!)

多个task并行执行会遇到一个问题,假设后续某个任务需要前面某个任务的结果,如果它们都同一时间执行,程序会崩溃的。那么,自然而然我们引出一个概念:stream。

==CUDA stream是将主机上的多个kernel进行某种顺序的排序。==

通过stream可以控制多个kernel在设备上的顺序。不同的stream互不影响。通常情况下,执行kernel比GPU-CPU传输数据的时间要长。因此,某些情况下可以将kernel的执行操作和数据传输放到不同的stream中,用kernel的时间掩盖传输时间,缩短程序运行时间。

通常情况下,执行kernel比GPU-CPU传输数据的时间要长。因此,某些情况下可以将kernel的执行操作和数据传输放到不同的stream中,用kernel的时间掩盖传输时间,缩短程序运行时间。

可以把传输数据的时间,湮灭掉。

CUDA stream可分为两种操作:

1、同步:此状态会阻塞CPU进程,直到kernel操作完成。

2、异步:此状态在唤醒kernel函数后立刻将控制权交给CPU。

还记得我们上一节介绍的SM等硬件的知识吗?这里也一样,从软件层面上看,不同的stream是同时进行的,但在硬件层面上还需要去争夺SM资源,可能需要相互等待。

GPU编程与优化实践 - 知乎 (zhihu.com)

CUDA stream可以显式或隐式调用。在前几张实例中,虽然我们在代码中没有任何stream的操作,但实际上系统会自动分配一个隐式stream,所有的kernel都在这一个stream上,如以下操作,cudaMemcpy会阻塞cpu进程,直到数据传输操作完成。

但假如我们相对多个kernel的执行顺序进行操作,则必须申请一个或多个显式stream进行管理。例如在以下情形中:

1、重叠host和device的计算。

2、重叠host计算与cpu-gpu的数据传输。

3、重叠device计算与cpu-gpu的数据传输。

4、gpu的并发计算

cudaMemcpyAsync可以执行异步操作,在数据传输过程中,将操作权交给cpu进行控制。

cudaError_t cudaMemcpyAsync(void* dst, const void* src, size_t count,
    cudaMemcpyKind kind, cudaStream_t stream = 0);

申请cuda stream:

cudaError_t cudaStreamCreate(cudaStream_t* pStream);

在异步传输数据时必须使用固定/不可分页的主机内存,可使用以下APT申请:

cudaError_t cudaMallocHost(void **ptr, size_t size);
cudaError_t cudaHostAlloc(void **pHost, size_t size, unsigned int flags);

image-20230925163452000

CUDA Events是cuda stream中的标记。(类似跑马拉松,把整个路程分为一段段的,每一段的路标就是一个events)Event只是一个标志,你可以用来查询stream的状态:

1、同步stream的执行

2、监控device进度

CUDA允许在任何一个点插入event,并查询该event是否完成。只有当该事件段中所有操作都被满足,event才会标志为完成。

events的声明,创建,销毁:

cudaEvent_t event;

cudaError_t cudaEventCreate(cudaEvent_t* event);

cudaError_t cudaEventDestroy(cudaEvent_t event);

CUDA ---- Stream and Event - 苹果妖 - 博客园 (cnblogs.com)

这个博客写的也非常系统:


请在这里,系统地学习CUDA编程

CUDA Zone - Library of Resources | NVIDIA Developer

CUDA Toolkit Documentation 12.2 (nvidia.com)


CUDA优化方案—stream的使用_cudastream_t_A晨的博客的博客-CSDN博客

image-20230925165243515

CUDA Stream的高阶用法:

//创建多个流
cudaStream_t stream[3];
for(int i=0; i<3; i++)
    cudaStreamCreat(&stream[i]);

float* hostPtr;
cudaMallocHost(&hostPtr, 3*size);
......
//开启了三个流,将数据分成三份分别交给了三个流进行计算
for(int i=0; i<3; i++){
    //从CPU内存复制数据到GPU显存
    cudaMemcpyAsync(DevPtr + i*size, hostPtr + i*size, size, cudaMemcpyHostToDevice, stream[i]);
    //执行Kernel函数
    Mykernel<<<grid, block, 0, stream[i]>>>(outputDevPtr + i*size, size, cudaMemcpyDeviceToHost, stream[i]);
    //将结果拷贝回CPU内存
    cudaMemcpyAsync(hostPtr + i*size, outputDevPtr + i*size, size, cudaMemcpyDeviceToHost, stream[i]);
}
//同步流
for(int i=0; i<3; i++)
    cudaStreamSynchronise(stream[i]);
//销毁流
for(int i=0; k<3; i++)
    cudaStreamDestroy(stream[i]);

}
### CUDA 流的概念 CUDA 流(Stream)是一种用于管理 GPU 上操作顺序和并发性的机制。通过使用多个流,开发者可以在不同流之间实现任务的重叠执行,从而提高硬件利用率和程序性能。 #### 基本概念 - **默认流**:每个 CUDA 上下文中都有一个默认流(Default Stream),它负责串行化所有提交给它的操作。如果未显式指定流,则这些操作会进入默认流[^1]。 - **异步行为**:除了默认流外,其他流都具有异步特性,这意味着当某个流中的操作被发起后,CPU 可以立即继续处理后续的任务而无需等待该操作完成。 #### 创建与销毁流 可以通过 `cudaStreamCreate` 函数创建一个新的流实例,并用 `cudaStreamDestroy` 销毁不再使用的流资源: ```c++ cudaStream_t stream; cudaStreamCreate(&stream); // 使用流... cudaStreamDestroy(stream); ``` #### 数据传输与核函数调用 在实际应用中,通常需要将主机内存的数据复制到设备端或者反过来;同时也可能涉及多次调用同一个或不同的 kernel 来完成复杂的计算逻辑。借助于自定义流,我们可以让上述过程更加高效地并行起来。例如下面的例子展示了如何利用两个独立的 streams 同时上传数据至GPU 并运行各自的 kernels: ```cpp float *d_A, *d_B; // Device pointers float *h_A, *h_B; // Host buffers filled with data... cudaMalloc((void**)&d_A, size); cudaMalloc((void**)&d_B, size); // Create two separate streams. cudaStream_t s1,s2; cudaStreamCreate(&s1); cudaStreamCreate(&s2); // Asynchronously copy A to device on first stream cudaMemcpyAsync(d_A,h_A,size,cudaMemcpyHostToDevice,s1); myKernel<<<blocksPerGridA,threadsPerBlockA,0,s1>>>(...parameters...) // Similarly do async operations related B but now using second stream 's2' cudaMemcpyAsync(d_B,h_B,size,cudaMemcpyHostToDevice,s2); anotherKernel<<<blocksPerGridB,threadsPerBlockB,0,s2>>>(...params...) // Cleanup resources after all work done ... cudaFree(d_A); cudaFree(d_B); cudaStreamDestroy(s1); cudaStreamDestroy(s2); ``` 这里需要注意的是,尽管来自不同streams 的指令可能会交错被执行,但是属于同一stream内的各项命令仍然保持其相对次序不变——即先发出的操作总是会在之后启动的那个之前结束。 #### 同步控制 为了协调多条流之间的相互作用关系,有时还需要引入额外的同步手段。这可通过事件(event)对象来达成目的。简单来说就是记录特定时刻的状态以便稍后再查询确认是否达到预期进展程度。 ```c++ cudaEvent_t event; cudaEventCreate(&event); someOperation<<<numBlocks,numThreads,sharedMemSize,customStream>>>(); cudaEventRecord(event, customStream); // Do other stuff here possibly involving different streams etc. // Later somewhere else where synchronization needed before proceeding further.. cudaEventSynchronize(event); cudaEventDestroy(event); ``` 以上介绍了关于CUDA Streams的一些基础知识及其典型应用场景下的编码实践方法论等内容[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值