简介
cudaSetDevice() 函数 详解 cudaSetDevice()
函数详解1. 作用 cudaSetDevice(int device)
用于设置当前 CPU 线程要使用的 GPU 设备。在一个系统中可能有多个 GPU,此函数可以让程序选择特定的 GPU 进行计算。
2. 语法 1 cudaError_t cudaSetDevice (int device) ;
device
:要选择的 GPU 设备的索引(从 0 开始)。
返回值:返回 cudaError_t
类型的错误码,若成功返回 cudaSuccess
。
3. 相关说明
必须在调用任何 CUDA API(如 cudaMalloc()
、cudaMemcpy()
)之前调用 cudaSetDevice()
,否则可能会导致错误或影响性能。
每个 CPU 线程只能绑定一个 GPU 设备,因此 cudaSetDevice()
仅影响当前线程。
如果没有调用 cudaSetDevice()
,CUDA 运行时默认选择 device 0
。
需要确保 device
值在可用的 GPU 范围内(可用 cudaGetDeviceCount()
获取 GPU 总数)。
4. 代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <cuda_runtime.h> #include <iostream> int main () { int deviceCount; cudaGetDeviceCount (&deviceCount); if (deviceCount == 0 ) { std::cerr << "No CUDA devices available." << std::endl; return -1 ; } int deviceId = 1 ; if (deviceId >= deviceCount) { std::cerr << "Invalid device ID." << std::endl; return -1 ; } cudaError_t err = cudaSetDevice (deviceId); if (err != cudaSuccess) { std::cerr << "cudaSetDevice failed: " << cudaGetErrorString (err) << std::endl; return -1 ; } std::cout << "Using GPU " << deviceId << std::endl; return 0 ; }
5. 获取当前设备 可以使用 cudaGetDevice()
获取当前线程绑定的 GPU:
1 2 3 int currentDevice;cudaGetDevice (¤tDevice);std::cout << "Current device: " << currentDevice << std::endl;
6. 线程与设备的关系
在 单线程程序 中,每个 CPU 线程只能使用一个 GPU。
在 多线程程序 中,不同的 CPU 线程可以绑定到不同的 GPU 以并行计算,但一个线程不能同时使用多个 GPU。
7. 可能的错误
错误代码
说明
cudaErrorInvalidDevice
device
号超出 GPU 数量范围
cudaErrorSetOnActiveProcess
当前进程已经使用了另一个 GPU(在某些情况下)
8. 结合 cudaGetDeviceCount()
在调用 cudaSetDevice()
之前,建议使用 cudaGetDeviceCount()
确保 device
值合法:
1 2 3 4 5 int deviceCount;cudaGetDeviceCount (&deviceCount);if (deviceCount > 0 ) { cudaSetDevice (0 ); }
9. 总结
cudaSetDevice()
用于选择 GPU,必须在 CUDA 相关 API 调用前使用。
仅对当前 CPU 线程生效。
在多 GPU 环境下,可结合 cudaGetDeviceCount()
和 cudaGetDevice()
确保选择有效设备。
这样可以有效管理多 GPU 计算资源,提高 CUDA 程序的灵活性和性能。 🚀
cudaMalloc() 函数 详解 cudaMalloc()
函数详解1. 作用 cudaMalloc()
用于在 GPU 设备(Device)上分配全局内存(Global Memory)。这是 CUDA 中最基础的内存管理函数之一。
2. 语法 1 cudaError_t cudaMalloc (void ** devPtr, size_t size) ;
参数
devPtr
:指向存储设备内存地址的指针变量(即指向 void*
的指针)。
size
:要分配的内存大小(以字节为单位)。
返回值
cudaSuccess
(0) :分配成功。
其他错误码 (如 cudaErrorMemoryAllocation
):分配失败。
3. 使用示例 (1) 在 GPU 设备上分配内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <cuda_runtime.h> #include <iostream> int main () { int * d_ptr; size_t size = 100 * sizeof (int ); cudaError_t err = cudaMalloc ((void **)&d_ptr, size); if (err != cudaSuccess) { std::cerr << "cudaMalloc failed: " << cudaGetErrorString (err) << std::endl; return -1 ; } std::cout << "Memory allocated on GPU." << std::endl; cudaFree (d_ptr); return 0 ; }
4. 相关注意事项 (1) 设备指针不能直接在主机(CPU)端访问 1 2 3 int * d_ptr;cudaMalloc ((void **)&d_ptr, 100 * sizeof (int ));std::cout << *d_ptr << std::endl;
正确做法 :使用 cudaMemcpy()
将数据拷贝到主机:
1 2 3 int h_val;cudaMemcpy (&h_val, d_ptr, sizeof (int ), cudaMemcpyDeviceToHost);std::cout << h_val << std::endl;
(2) 设备内存需要手动释放
使用 cudaFree()
释放内存:
如果忘记释放,会导致 显存泄漏(Memory Leak) 。
(3) 检查 cudaMalloc()
是否成功
可能的错误
cudaErrorMemoryAllocation
:显存不足
cudaErrorInvalidValue
:size
为 0
cudaErrorInvalidDevicePointer
:传入的指针非法
建议:始终检查返回值 1 2 3 if (cudaMalloc ((void **)&d_ptr, size) != cudaSuccess) { std::cerr << "Memory allocation failed!" << std::endl; }
(4) 计算设备内存的使用情况
cudaMemGetInfo()
可用于查询 GPU 剩余内存:1 2 3 4 size_t freeMem, totalMem;cudaMemGetInfo (&freeMem, &totalMem);std::cout << "Free memory: " << freeMem / (1024 * 1024 ) << " MB, " << "Total memory: " << totalMem / (1024 * 1024 ) << " MB" << std::endl;
(5) 多 GPU 环境
cudaMalloc()
仅在当前设备上分配内存。
若使用多 GPU,可先调用 cudaSetDevice(deviceId)
切换 GPU:1 2 cudaSetDevice (1 ); cudaMalloc ((void **)&d_ptr, size);
5. cudaMalloc()
与其他 CUDA 内存管理函数的比较
函数
作用
适用场景
释放方法
cudaMalloc()
设备全局内存分配
GPU 计算数据存储
cudaFree()
cudaMallocHost()
主机页锁定内存(可用于异步拷贝)
cudaMemcpyAsync()
cudaFreeHost()
cudaMallocManaged()
统一内存(主机和设备共享)
统一内存编程
cudaFree()
cudaMallocPitch()
2D 数组分配,优化对齐
处理图像等 2D 数据
cudaFree()
6. 总结
cudaMalloc()
在 GPU 上分配全局内存,返回设备指针。
不能直接访问设备指针,需要使用 cudaMemcpy()
进行数据传输。
需要手动释放 cudaFree()
,否则会导致显存泄漏。
在多 GPU 环境下,应先调用 cudaSetDevice()
选择目标 GPU。
合理使用 cudaMalloc()
进行显存管理,可以提高 CUDA 程序的性能和稳定性。 🚀
cudaStreamCreate() 函数 详解 cudaStreamCreate()
函数详解1. 作用 cudaStreamCreate()
用于创建 CUDA 流(Stream) ,流是一种异步执行的队列,可以在同一 GPU 上同时执行多个任务,实现 并行计算 和 异步执行 。
2. 语法 1 cudaError_t cudaStreamCreate (cudaStream_t* pStream) ;
参数
pStream
:指向 cudaStream_t
类型的指针,用于存储创建的流的句柄。
返回值
cudaSuccess
:流创建成功。
其他 cudaError_t
错误码:创建失败,例如 内存不足 或 CUDA 上下文问题 。
3. 使用示例 (1) 创建并使用 CUDA 流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <cuda_runtime.h> #include <iostream> int main () { cudaStream_t stream; cudaError_t err = cudaStreamCreate (&stream); if (err != cudaSuccess) { std::cerr << "cudaStreamCreate failed: " << cudaGetErrorString (err) << std::endl; return -1 ; } std::cout << "CUDA Stream created successfully!" << std::endl; cudaStreamDestroy (stream); return 0 ; }
4. 相关函数 (1) cudaStreamDestroy()
- 销毁 CUDA 流 每个创建的 CUDA 流在使用后应当释放:
1 cudaStreamDestroy (stream);
(2) cudaMemcpyAsync()
- 异步拷贝 流可以与 cudaMemcpyAsync()
结合,实现 异步数据拷贝 :
1 2 3 4 5 int * d_data;int h_data[100 ];cudaMalloc ((void **)&d_data, 100 * sizeof (int ));cudaMemcpyAsync (d_data, h_data, 100 * sizeof (int ), cudaMemcpyHostToDevice, stream);
(3) cudaStreamSynchronize()
- 等待流完成 使用 cudaStreamSynchronize()
可以等待特定流中的任务执行完毕:
1 cudaStreamSynchronize (stream);
5. CUDA 流的特点 (1) 默认流(cudaStream_t(0)
)
如果 不指定流 ,CUDA 内核和 cudaMemcpy()
运行在 默认流(默认队列) 中,按顺序执行:1 2 3 cudaMemcpy (d_data, h_data, 100 * sizeof (int ), cudaMemcpyHostToDevice);kernel<<<grid, block>>>(d_data); cudaMemcpy (h_data, d_data, 100 * sizeof (int ), cudaMemcpyDeviceToHost);
这些操作会 按顺序执行 ,下一个任务必须等待上一个任务完成。
(2) 用户定义流(异步执行)
使用 cudaStreamCreate()
创建 多个流 ,可在同一 GPU 设备上同时执行 多个任务 :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cudaStream_t stream1, stream2; cudaStreamCreate (&stream1);cudaStreamCreate (&stream2);cudaMemcpyAsync (d_data1, h_data1, size, cudaMemcpyHostToDevice, stream1);cudaMemcpyAsync (d_data2, h_data2, size, cudaMemcpyHostToDevice, stream2);cudaStreamSynchronize (stream1);cudaStreamSynchronize (stream2);cudaStreamDestroy (stream1);cudaStreamDestroy (stream2);
在这种情况下,数据拷贝和计算可以并行执行 ,提高 GPU 利用率。
6. 使用多个 CUDA 流的应用场景
场景
说明
数据拷贝与计算重叠
cudaMemcpyAsync()
和 Kernel 在不同流中执行,提高效率
多 Kernel 并行执行
多个 Kernel 任务放入不同流,提高计算吞吐量
多线程任务调度
在多线程程序中,每个线程管理一个 CUDA 流
7. cudaStreamCreate()
可能的错误
错误码
说明
cudaErrorInvalidValue
pStream
为空指针
cudaErrorMemoryAllocation
内存不足,无法创建流
cudaErrorInitializationError
CUDA 运行时未正确初始化
8. 总结
cudaStreamCreate()
创建 CUDA 流 ,用于异步执行任务。
任务可使用 cudaMemcpyAsync()
或 cudaLaunchKernel()
提交到特定流。
使用 cudaStreamSynchronize()
等待流完成,使用 cudaStreamDestroy()
释放资源。
适用于 数据传输与计算重叠、Kernel 并行执行、多线程任务调度 等场景。
合理使用 CUDA 流,可以 提升 GPU 并行计算效率 ,加速深度学习、科学计算等应用!🚀
cudaMemcpyAsync() 函数 详解 cudaMemcpyAsync()
函数详解1. 作用 cudaMemcpyAsync()
用于在 主机(Host) 和 设备(Device) 之间进行 异步 数据拷贝,与 cudaMemcpy()
不同,它不会阻塞 CPU,而是将拷贝任务放入指定的 CUDA 流(Stream) 中,从而实现 数据传输与计算的重叠 ,提高 GPU 计算效率。
2. 语法 1 cudaError_t cudaMemcpyAsync (void * dst, const void * src, size_t count, cudaMemcpyKind kind, cudaStream_t stream) ;
参数
dst
:目标地址(可以是主机或设备内存)。
src
:源地址(可以是主机或设备内存)。
count
:拷贝的数据大小(以字节为单位)。
kind
:数据拷贝的方向,cudaMemcpyKind
枚举值:
cudaMemcpyHostToDevice
(H→D):从主机拷贝到设备
cudaMemcpyDeviceToHost
(D→H):从设备拷贝到主机
cudaMemcpyDeviceToDevice
(D→D):在设备内存之间拷贝
cudaMemcpyHostToHost
(H→H):在主机内存之间拷贝
stream
:指定 CUDA 流 ,数据拷贝任务将在该流中执行(可使用 0
代表 默认流 )。
返回值
**cudaSuccess
**:拷贝成功。
其他 cudaError_t
错误码:如 cudaErrorInvalidValue
(参数无效)。
3. 代码示例 (1) 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <cuda_runtime.h> #include <iostream> int main () { cudaStream_t stream; cudaStreamCreate (&stream); int h_data[100 ], h_result[100 ]; int * d_data; size_t size = 100 * sizeof (int ); cudaMalloc ((void **)&d_data, size); cudaMemcpyAsync (d_data, h_data, size, cudaMemcpyHostToDevice, stream); cudaMemcpyAsync (h_result, d_data, size, cudaMemcpyDeviceToHost, stream); cudaStreamSynchronize (stream); cudaFree (d_data); cudaStreamDestroy (stream); std::cout << "Async memcpy completed!" << std::endl; return 0 ; }
在 cudaMemcpyAsync()
执行时,CPU 不会等待 数据拷贝完成,而是继续执行后续代码。只有当 cudaStreamSynchronize()
被调用时,才会阻塞 CPU 直到流中的任务完成。
4. cudaMemcpyAsync()
与 cudaMemcpy()
的区别
特性
cudaMemcpy()
cudaMemcpyAsync()
同步/异步
同步 (阻塞 CPU)
异步 (不阻塞 CPU)
默认流
使用默认流 0
可以指定流
计算与拷贝重叠
❌ 不支持
✅ 支持 (需要 cudaMemcpyAsync()
+ cudaStreamSynchronize()
)
适用场景
简单数据拷贝
需要 并行执行计算 & 传输 的情况
5. cudaMemcpyAsync()
的高级应用 (1) 计算与数据拷贝重叠 cudaMemcpyAsync()
的主要优势是 可以与 Kernel 并行执行 ,提高 GPU 计算吞吐量。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <cuda_runtime.h> #include <iostream> __global__ void kernel (int * d_data) { int idx = threadIdx.x + blockIdx.x * blockDim.x; d_data[idx] *= 2 ; } int main () { cudaStream_t stream; cudaStreamCreate (&stream); int h_data[100 ], h_result[100 ]; int * d_data; size_t size = 100 * sizeof (int ); cudaMalloc ((void **)&d_data, size); cudaMemcpyAsync (d_data, h_data, size, cudaMemcpyHostToDevice, stream); kernel<<<1 , 100 , 0 , stream>>>(d_data); cudaMemcpyAsync (h_result, d_data, size, cudaMemcpyDeviceToHost, stream); cudaStreamSynchronize (stream); cudaFree (d_data); cudaStreamDestroy (stream); return 0 ; }
在这里:
数据拷贝 (cudaMemcpyAsync()
)
计算 Kernel 执行 (kernel<<<>>>
)
拷贝结果回主机 (cudaMemcpyAsync()
) 都在 同一流 (stream
)中执行,CPU 不会等待 ,而是继续执行后续代码,最终 cudaStreamSynchronize()
才会阻塞 CPU,等待所有任务完成。
(2) 使用多个流并行执行任务 在多流(Multi-stream)环境下,多个 cudaMemcpyAsync()
可用于 并行数据传输 :
1 2 3 4 5 6 7 8 9 10 11 12 cudaStream_t stream1, stream2; cudaStreamCreate (&stream1);cudaStreamCreate (&stream2);cudaMemcpyAsync (d_data1, h_data1, size, cudaMemcpyHostToDevice, stream1);cudaMemcpyAsync (d_data2, h_data2, size, cudaMemcpyHostToDevice, stream2);cudaStreamSynchronize (stream1);cudaStreamSynchronize (stream2);cudaStreamDestroy (stream1);cudaStreamDestroy (stream2);
流 stream1
传输 d_data1
,流 stream2
传输 d_data2
,两个拷贝任务可同时进行。
如果不使用流,CUDA 任务将会串行执行,不能充分利用 GPU 带宽。
6. cudaMemcpyAsync()
可能的错误
错误代码
说明
cudaErrorInvalidValue
参数无效(如 dst
或 src
为空指针)
cudaErrorInvalidMemcpyDirection
cudaMemcpyKind
不匹配(如 cudaMemcpyDeviceToHost
但 dst
是设备指针)
cudaErrorMemoryAllocation
设备内存不足
cudaErrorIllegalAddress
访问了未分配或非法的 GPU 地址
7. cudaMemcpyAsync()
适用场景
应用场景
说明
数据传输与计算并行
异步拷贝 + Kernel 计算,减少 CPU/GPU 等待时间
多流任务调度
在不同流中执行不同的任务,提高 GPU 利用率
批量数据处理
预加载数据,提高数据吞吐量
8. 总结
cudaMemcpyAsync()
异步 进行数据拷贝,不会阻塞 CPU。
需要 指定 CUDA 流 ,否则默认使用 0
(默认流)。
需要 配合 cudaStreamSynchronize()
确保数据正确性。
适用于 数据传输 + 计算并行执行 ,提高 GPU 计算吞吐量。
合理使用 cudaMemcpyAsync()
可以 提升 CUDA 应用的性能 ,尤其在 深度学习、科学计算、大规模数据处理 等场景下表现突出!🚀