当前位置: 首页 > news >正文

cuda06- 流 并发

目录

  • 1. CUDA流
    • 1.1 流的定义
    • 1.3 流优先级
    • 1.4 cuda事件Event
    • 1.4 stream同步
      • 1.4.1 阻塞流和非阻塞流
      • 1.4.2 隐式同步
      • 1.4.3 显式同步
      • 1.4.4 配置event
    • 1.5 并发执行
      • 1.5.1 虚假依赖关系
      • 1.5.2 openMP优化并行计算
      • 1.5.3 使用环境变量调整流行为
      • 1.5.4 创建流之间的依赖关系
      • 1.5.5 空流的阻塞行为
    • 1.6 重叠内存拷贝和壳函数执行
      • 1.6.1 使用深度有先的调度方法
      • 1.6.2 使用广度有先的调度方法
    • 重叠cpu和gpu的运行
    • 流回调函数接口

博主公司重组,求推荐大模型部署ai infra, base上海的工作。my phone 15601237103

1. CUDA流

1.1 流的定义

空流,即隐式声明的流。内核启动和数据cp默认使用空流。
非空流, 使用cudaStreamCreate开辟的流。

  • 支持的并发类型:

主机计算 和 设备计算的重叠
主机计算 和 主机设与备之间数据传输
主机设与备之间数据传输 和 设备计算
并发设备计算

  • 异步数据传输:
__host__ ​__device__​cudaError_tcudaMemcpyAsync(void*dst,constvoid*src,size_tcount,cudaMemcpyKind kind,cudaStream_tstream=0)

当使用cudaMemcpyAsync做异步传输时候,必须要使用固定主机内存。可以使用

cudaMallocHost cudaHostAlloc
  • 在非默认流中启动内核,必须要提供流作为型参
kernel_name<<<grid,block,sharedMemSize,stream>>>(args);
  • 非默认流的声明 和 释放
cudaStream stream;cudaStreamCreate(&stream);cudaStreamDestroy(stream);

cudaStreamDestroy调用完后会立即返回,流相关的资源会被自动释放。

  • 检查流中所有操作是否全部完成
cudaStreamSynchronize(stream);cudaStreamQuery(stream);

cudaStreamSynchronize阻塞主机直到操作完成。cudaStreamQuery立即返回。

Fermi架构支持16路stream并发。
Kepler架构支持16路stream并发。

1.3 流优先级

  • 创建指定优先级的流
cudaStreamCreateWithPriority
  • 获取流允许优先级的范围
cudaDeviceGetStreamPriorityRange

1.4 cuda事件Event

Event使用场景:同步流执行。
Event使用场景: 监控设备进展,在stream中插入一个点用于监控流操作是否已经到达指定点。

  • 流创建和销毁
__host__​cudaError_t cudaEventCreate(cudaEvent_t*event)__host__​__device__​cudaError_t cudaEventDestroy(cudaEvent_t event)
  • 流同步
__host__​cudaError_t cudaEventSynchronize(cudaEvent_t event)
  • 两个事件的运行时间
__host__​__device__​cudaError_t cudaEventRecord(cudaEvent_t event, cudaStream_t stream=0)__host__​cudaError_t cudaEventElapsedTime(float* ms, cudaEvent_t start, cudaEvent_t end)

1.4 stream同步

非空流是异步流。
空流/默认流是同步流。
非空流包含 : 阻塞流和非阻塞流。

1.4.1 阻塞流和非阻塞流

__host__​__device__​cudaError_t cudaStreamCreateWithFlags(cudaStream_t* pStream, unsigned int flags)

CudastreamDefault :默认流创建标志。
cudaStreamNonBlocking: 非阻塞流。

1.4.2 隐式同步

调用cudaMemcpy函数,可以隐式同步设备和主机。
包含隐式同步的操作:
锁页主机主机内存分配
设备内存分配
设备内存初始化
同一个设备上两个地址之间的内存复制
一级缓存/共享内存配置的修改

1.4.3 显式同步

__host__​__device__​cudaError_t cudaDeviceSynchronize(void)__host__​cudaError_t cudaStreamSynchronize(cudaStream_t stream)__host__​cudaError_t cudaEventSynchronize(cudaEvent_t event)

stream中等待event

cudaError_tcudaStreamWaitEvent(cudaStream_tstream,cudaEvent_tevent,unsignedintflags);

CUDA 流(stream)等待某个事件(event)完成,当 event 完成后,stream 才会继续执行后续的操作。

1.4.4 配置event

__host__​__device__​cudaError_tcudaEventCreateWithFlags(cudaEvent_t*event,unsignedintflags)Valid flags include:cudaEventDefault:Default.使用该事件来准确地测量 CUDA 操作的执行时间,并且可以在事件完成后进行同步 cudaEventBlockingSync:阻塞式同步,直到事件完成 A host thread that usescudaEventSynchronize()to wait on an event created with this flag will block until the event actually completes.cudaEventDisableTiming:只同步,不记录时间戳,will provide the best performance when used withcudaStreamWaitEvent()andcudaEventQuery().cudaEventInterprocess:可以用于进程间事件。Specifies that the created event may be used as an interprocess event bycudaIpcGetEventHandle().

1.5 并发执行

1.5.1 虚假依赖关系

虚假依赖关系:所有的stream队列要复用到一个硬件队列中,深度优先启动的流就阻塞了后面其他流。

深度优先启动内核函数。排布在一起的K2 K3来自同一个stream,形成虚假依赖关系

for(inti=0;i<n_streams;i++){kernel_1<<<grid,block,0,streams[i]>>>();kernel_2<<<grid,block,0,streams[i]>>>();kernel_3<<<grid,block,0,streams[i]>>>();kernel_4<<<grid,block,0,streams[i]>>>();}


nvvp抓流

广度优先启动内核函数。确保每个kernel函数来自不同的stream,相邻的任务不存在虚假依赖关系。

for(inti=0;i<n_streams;i++)kernel_1<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams;i++)kernel_2<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams;i++)kernel_3<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams;i++)kernel_4<<<grid,block,0,streams[i]>>>();


nvvp显示并发性更好一点? todo,我的抓图和书上的不同。

1.5.2 openMP优化并行计算

OpenMP:采用基于指令(pragma)的编程模型,通过在源代码中插入特定的编译指令来指示编译器如何并行化代码。
优化kernel的启动部分

omp_set_num_threads(n_streams);#pragmaomp parallel{inti=omp_get_thread_num();kernel_1<<<grid,block,0,streams[i]>>>();kernel_2<<<grid,block,0,streams[i]>>>();kernel_3<<<grid,block,0,streams[i]>>>();kernel_4<<<grid,block,0,streams[i]>>>();}

在时间轴上并没有看到任何并行优化的表现…,似乎并不如手写广度优先的展开。

1.5.3 使用环境变量调整流行为

我们设置最大连接数为4, stream数量为8,

#defineNSTREAM8constchar*iname="CUDA_DEVICE_MAX_CONNECTIONS";setenv(iname,"4",1);

我的期望是流的数量超过了最大连接数,那么多个流会共享一个连接,下图不符合预期。

书本上的预期应该是这样子:

1.5.4 创建流之间的依赖关系

原代码是这样子。

for(inti=0;i<n_streams;i++){kernel_1<<<grid,block,0,streams[i]>>>();kernel_2<<<grid,block,0,streams[i]>>>();kernel_3<<<grid,block,0,streams[i]>>>();kernel_4<<<grid,block,0,streams[i]>>>();CHECK(cudaEventRecord(kernelEvent[i],streams[i]));CHECK(cudaStreamWaitEvent(streams[n_streams-1],kernelEvent[i],0));}

nvvp抓流是这样效果,

但是预期应该是前三路并行,第四路等待前三路完成后执行:

代码我改成这样:

for(inti=0;i<n_streams-1;i++)kernel_1<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams-1;i++)kernel_2<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams-1;i++)kernel_3<<<grid,block,0,streams[i]>>>();for(inti=0;i<n_streams-1;i++){kernel_4<<<grid,block,0,streams[i]>>>();CHECK(cudaEventRecord(kernelEvent[i],streams[i]));}CHECK(cudaStreamWaitEvent(streams[n_streams-1],kernelEvent[2],0));CHECK(cudaStreamWaitEvent(streams[n_streams-1],kernelEvent[1],0));CHECK(cudaStreamWaitEvent(streams[n_streams-1],kernelEvent[0],0));kernel_1<<<grid,block,0,streams[3]>>>();kernel_2<<<grid,block,0,streams[3]>>>();kernel_3<<<grid,block,0,streams[3]>>>();kernel_4<<<grid,block,0,streams[3]>>>();

才可以看到流等待事件的效果:

1.5.5 空流的阻塞行为

空流是一个 阻塞流,这意味着它会阻塞其他非空流的执行,直到空流中的操作完成。
空流中的操作是同步的,即它们会按照提交的顺序依次执行。
优先并行考虑应该使用非空流:

cudaStreamCreate(&stream1);cudaStreamCreateWithFlags(&stream,cudaStreamNonBlocking);

1.6 重叠内存拷贝和壳函数执行

1.6.1 使用深度有先的调度方法


可以看到并行情况:
不同流中内核并行,
内核和其他流中数据拷贝并行,
不同方向(H2D,D2H)的数据拷贝并行

1.6.2 使用广度有先的调度方法


我的测试表示:广度优先调度多个内核的方法,耗时更少。虽然H2D的cp没有和其他流上内核执行出现并行,但是我的这个显卡内核并行度更高,也许这就是广度更快的原因。

重叠cpu和gpu的运行

所有的内核启动默认情况下都是异步的。
cudaMemcpyAsync,如果要实现异步(数据cp和计算的异步),要使用cudaMallocHost申请的page-lock内存。
否则,使用非锁页内存(如malloc)无法实现异步,只能达到cudaMemcpy的同步效果,不会报错。

流回调函数接口

回调函数目前没有看到太多使用场景

voidCUDART_CBmy_callback(cudaStream_tstream,cudaError_tstatus,void*data){printf("callback from stream %d\n",*((int*)data));}for(inti=0;i<n_streams;i++){stream_ids[i]=i;kernel_1<<<grid,block,0,streams[i]>>>();kernel_2<<<grid,block,0,streams[i]>>>();kernel_3<<<grid,block,0,streams[i]>>>();kernel_4<<<grid,block,0,streams[i]>>>();CHECK(cudaStreamAddCallback(streams[i],my_callback,(void*)(stream_ids+i),0));}
http://www.jsqmd.com/news/1120819/

相关文章:

  • 3个技巧让你在Minecraft中实现跨平台地图编辑:Amulet-Map-Editor完全指南
  • Mermaid在线编辑器:为什么这是你告别复杂绘图软件的最佳选择?
  • 15分钟极速部署:TrueNAS Scale上搭建高性能Minecraft Forge服务器全指南
  • Mermaid在线编辑器完整指南:5个实用技巧制作专业图表
  • Qwable-9B-Claude-Fable-5-StraTA-i1-GGUF完全解析:革命性量化模型如何重塑AI部署效率
  • Java开发入门:从零开始构建第一个RESTAPI
  • 如何轻松永久保存微信聊天记录?WeChatMsg完整数据导出与智能分析完全指南
  • gsplat完整指南:如何快速掌握CUDA加速的高斯溅射技术
  • Claude Opus 4.7深度评测:上下文稳定性与推理深度退化实测
  • 百度网盘macOS插件破解指南:3步解锁SVIP高速下载功能
  • oXygen XML Editor—— XML编辑与结构化内容管理的优秀工具
  • 终极PDF解析方案:AnythingLLM如何让复杂文档「开口说话」
  • 如何用85%准确率的AI模型预测股票走势?Kronos金融时间序列预测模型深度解析
  • HsMod:炉石传说终极增强插件,55个功能让游戏体验全面升级
  • 如何快速集成多语言支持:CozoDB嵌入式部署完整指南
  • 终极懒猫助手:3步打造整洁如新的浏览器书签库
  • 如何快速融入已有一期的项目并参与二期开发
  • DevExpress WinForms中文教程:Grid View - 如何实现识别行操作?(一)
  • Faster-Whisper终极指南:4倍速本地语音识别技术深度解析
  • Umi-OCR Windows 7环境部署与性能调优技术指南
  • GPT-6寂寞感背后:大模型落地的四重错位与务实破局路径
  • 双向全桥LLC谐振变换器在新能源并网中的应用
  • 从零到专业:3步掌握Lean量化交易引擎,打造你的智能交易系统
  • 终极指南:如何彻底解决OSX-KVM虚拟机的音频延迟问题
  • 近期AI量化学习,按四个阶段检查风险
  • 3个关键突破:Mermaid Live Editor如何用代码思维重构技术图表创作流程
  • Dify实战指南:从零部署到构建AI工作流与RAG应用
  • 如何使用Git
  • 界面控件Kendo UI for Vue 2024 Q4亮点 - 增强图表的数据模板、导航功能
  • 终极Kitty终端配置指南:10倍效率提升的macOS专属优化方案