调用cudaGraphicsGLRegisterBuffer后CUDA内存占用翻倍问题及规避咨询
问题
对大小为sz的OpenGL缓冲区调用cudaGraphicsGLRegisterBuffer后,通过cudaMemGetInfo检测到CUDA内存占用量为缓冲区大小的两倍。该问题可通过修改CUDA官方simpleGL示例中的createVBO函数复现,修改后的代码如下:
void createVBO(GLuint *vbo, struct cudaGraphicsResource **vbo_res, unsigned int vbo_res_flags) { assert(vbo); size_t freeM(0), totalM(0); cudaMemGetInfo(&freeM, &totalM); size_t freeMPre = freeM; // create buffer object glGenBuffers(1, vbo); glBindBuffer(GL_ARRAY_BUFFER, *vbo); // initialize buffer object unsigned int size = mesh_width * mesh_height * 4 * sizeof(float) * 10240; glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); cudaDeviceSynchronize(); printf(" ================ vbo size: %lu \n", size); cudaMemGetInfo(&freeM, &totalM); printf(" ================ create vbo cuda mem: %lu \n", (freeMPre - freeM)); freeMPre = freeM; // register this buffer object with CUDA checkCudaErrors(cudaGraphicsGLRegisterBuffer(vbo_res, *vbo, vbo_res_flags)); cudaMemGetInfo(&freeM, &totalM); auto regCudaMem = freeMPre - freeM; printf(" ================ RegisterBuffer cuda mem: %lu \n", (freeMPre - freeM)); freeMPre = freeM; printf("registered cuda size / vbo size = %f\n", (float)regCudaMem / (float)size); SDK_CHECK_ERROR_GL(); }
测试结果中registered cuda size / vbo size的数值始终为2。由于应用需要分配超大缓冲区用于CUDA与OpenGL互操作,这种内存翻倍的情况无法承受。请问这是CUDA与OpenGL互操作的固有行为吗?该如何规避?
回答
- 这并非CUDA与OpenGL互操作的固有行为,是特定场景下的内存临时分配、驱动对齐机制或测试时机导致的表象。
- 规避方法:
- 延迟映射后检查内存:
cudaGraphicsGLRegisterBuffer仅完成资源注册,不会立即分配CUDA侧内存,真正的内存映射发生在首次调用cudaGraphicsMapResources时。调整测试逻辑,在首次映射后再通过cudaMemGetInfo检查,此时内存占用会接近缓冲区实际大小。 - 更换GL存储模式:创建VBO时,若使用场景允许,用
GL_STREAM_DRAW或GL_STATIC_DRAW替代GL_DYNAMIC_DRAW,部分驱动会针对不同存储模式优化内存分配策略,减少冗余。 - 升级驱动与匹配GPU架构:老旧驱动或低端GPU可能存在内存分配冗余,升级到最新NVIDIA驱动;同时确认GPU支持的CUDA特性(如统一内存),利用更高效的互操作模式。
- 使用统一内存:对于Kepler及以上架构的GPU,直接分配CUDA统一内存,再通过
cudaGraphicsGLRegisterBuffer关联,避免额外的内存拷贝或冗余分配。 - 手动对齐缓冲区大小:通过
cudaDeviceGetAttribute查询设备的内存对齐要求,创建VBO时手动将缓冲区大小对齐到要求的倍数,减少驱动为满足对齐而额外分配的内存。
- 延迟映射后检查内存:
内容的提问来源于stack exchange,提问作者Jimmy Chen
相关产品推荐
相关产品推荐

