OpenCL程序同时占用主机与设备内存的原因及优化问询
先聊聊你遇到的现象背后的原因,再给你针对性的解决办法:
一、为什么会同时占用主机和设备内存?
1. NVIDIA GTX 750(独立GPU)的情况
GTX750是带独立显存的离散GPU,你创建CL_MEM_READ_WRITE类型的缓冲区时,驱动会在设备显存中分配对应的内存。但同时,很多OpenCL驱动(尤其是NVIDIA的实现)默认会在主机内存中创建一份影子缓冲区(Shadow Buffer)——这是驱动为了优化数据传输效率做的设计:当你调用clEnqueueWriteBuffer/clEnqueueReadBuffer时,驱动可以直接使用这份主机端的镜像来快速完成数据拷贝,避免每次传输都临时分配内存。这份影子缓冲区的大小和你创建的设备缓冲区完全一致,所以你看到两边都增长了约600MB。
2. 骁龙820(Adreno GPU)的情况
骁龙820采用统一内存架构(UMA),GPU没有独立的显存,所有设备内存都是直接从系统内存(主机内存)中划分的。所以你创建的OpenCL缓冲区本质上就是系统内存的一部分,自然只会看到主机内存增长,而没有独立GPU显存的变化。
二、如何减少/避免主机内存占用?
1. 明确告诉驱动不需要主机端访问缓冲区
如果你只通过clEnqueueWriteBuffer/clEnqueueReadBuffer来传输数据,不需要直接映射缓冲区到主机地址空间(比如用clEnqueueMapBuffer),那么可以在创建缓冲区时添加CL_MEM_HOST_NO_ACCESS标志。这个标志会告诉驱动:主机不会直接访问这块内存,不需要创建影子缓冲区,从而避免主机端的额外内存占用。
修改你的缓冲区创建代码:
g_mem_under_down_lap_pyr_32FC3 = clCreateBuffer(g_ctx, CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS, g_sizeMgr->getPyrCapacity() * 3 * sizeof(float), nullptr, &err); g_mem_mean_down_lap_pyr_32FC3 = clCreateBuffer(g_ctx, CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS, g_sizeMgr->getPyrCapacity() * 3 * sizeof(float), nullptr, &err);
2. 优化数据传输的方式
- 尽量使用非阻塞传输:调用
clEnqueueWriteBuffer/clEnqueueReadBuffer时,将blocking_write/blocking_read参数设为CL_FALSE,然后通过事件(clWaitForEvents)或者clFinish来同步。这样可以避免驱动为阻塞传输临时分配主机内存中转。 - 坚持复用缓冲区:你现在在
init_opencl阶段预创建所有缓冲区的做法非常好,避免了每次process都重复分配释放内存,继续保持这个设计。
3. 针对骁龙820的UMA架构优化
因为UMA下设备内存就是系统内存,无法完全避免占用主机内存,但可以减少冗余分配:
使用CL_MEM_USE_HOST_PTR标志,直接复用你预先分配的主机内存作为设备缓冲区。这样驱动不会额外分配内存,而是直接使用你提供的主机内存空间,减少内存拷贝和冗余占用:
// 预先在主机端分配内存 size_t buf_size = g_sizeMgr->getPyrCapacity() * 3 * sizeof(float); float* host_under_buf = (float*)malloc(buf_size); float* host_mean_buf = (float*)malloc(buf_size); // 创建缓冲区时指定主机指针 g_mem_under_down_lap_pyr_32FC3 = clCreateBuffer(g_ctx, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, buf_size, host_under_buf, &err); g_mem_mean_down_lap_pyr_32FC3 = clCreateBuffer(g_ctx, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, buf_size, host_mean_buf, &err);
注意:使用这个标志后,主机内存的释放要等到你调用clReleaseMemObject之后,避免出现悬空指针。
内容的提问来源于stack exchange,提问作者rzwmwhyy




