cudaStreamCreate必须传地址(如&stream),否则因句柄未初始化导致cudaErrorInvalidValue或崩溃;需用cudaMallocHost分配页锁定内存以支持cudaMemcpyAsync;避免混用默认流,所有操作应显式指定非默认流;同步应使用cudaStreamSynchronize而非cudaDeviceSynchronize;跨流依赖须用事件显式同步。

cudaStreamCreate 创建流时必须传地址,否则段错误
常见错误是把 cudaStream_t 变量直接传给 cudaStreamCreate,比如写成 cudaStreamCreate(stream)。实际必须传指针:cudaStreamCreate(&stream)。否则函数无法写入句柄值,后续所有流操作(如内核启动、cudaMemcpyAsync)都会因流句柄为未初始化的垃圾值而失败,典型表现是 cudaErrorInvalidValue 或静默崩溃。
创建多个流时,每个都要独立调用 cudaStreamCreate 并检查返回值:
cudaStream_t stream_a, stream_b;-
cudaError_t err_a = cudaStreamCreate(&stream_a);// 必须检查 err_a -
cudaError_t err_b = cudaStreamCreate(&stream_b);// 同样不能忽略
cudaMemcpyAsync 要求主机内存页锁定,否则同步失败
用 cudaMemcpyAsync 前,主机内存必须是 pinned memory(页锁定内存)。普通 malloc 或 new 分配的内存不支持异步拷贝,强行使用会导致操作退化为同步行为,甚至触发 cudaErrorInvalidValue。
正确做法是用 cudaMallocHost 或 cudaHostAlloc 分配主机端缓冲区:
-
float* h_data; cudaMallocHost(&h_data, size);// 推荐,自动分配可分页锁定内存 -
cudaHostAlloc(&h_data, size, cudaHostAllocDefault);// 等效 - 对应释放用
cudaFreeHost(h_data),不是free
这点容易被忽略——即使流创建成功、内核能跑,只要拷贝用的是普通内存,重叠就根本不会发生,整个流水线卡在 H2D 阶段。
默认流会阻塞所有非默认流,别混用
默认流(即不传流参数的 cudaMemcpy 或核函数启动)有特殊语义:它会等待所有其他流完成当前任务后才开始执行;同时,它运行期间,其他流也会被阻塞。这意味着一旦你混用默认流和自定义流,计算与传输重叠就彻底失效。
务必统一使用非默认流:
- 所有
cudaMemcpyAsync显式传入stream_a或stream_b - 所有核函数启动必须带第四个参数:
kernel>>() - 避免任何不带流参数的 CUDA API 调用(包括
cudaDeviceSynchronize()这种全局同步,除非真需要)
一个典型陷阱是:调试时加了 printf 后顺手调用 cudaMemcpy 拷结果回主机——这个没指定流,就掉进默认流陷阱里了。
同步必须按流粒度做,cudaStreamSynchronize 是关键关卡
流之间彼此独立,但你的 CPU 主线程需要知道某一流何时就绪。不能只靠 cudaDeviceSynchronize(),它会等所有流,拖慢整体吞吐。应该对每个流单独同步:
-
cudaStreamSynchronize(stream_a)—— 阻塞直到 stream_a 中所有操作完成 -
cudaStreamQuery(stream_b)—— 非阻塞轮询,返回cudaSuccess或cudaErrorNotReady,适合做状态检查
注意:流销毁前必须确保它已空闲,否则 cudaStreamDestroy 行为未定义。所以典型收尾顺序是:cudaStreamSynchronize → cudaStreamDestroy。
最易被忽略的一点:流内操作虽按序执行,但不同流间无执行顺序保证。如果你依赖 stream_b 的输出作为 stream_a 的输入,不能只靠“先启 b 后启 a”来假设顺序——必须用 cudaEventRecord + cudaStreamWaitEvent 显式建依赖,否则结果不可预测。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











