cudaErrorMemoryAllocation错误恢复:cudaMallocHost失败后无法分配内存
我在应用中用cudaMallocHost分配内存,分配量由用户选择,可能因内存不足返回cudaErrorMemoryAllocation错误。此时调用cudaFreeHost也返回该错误(根据CUDA文档,它可能返回之前异步启动的错误码)。
问题:自此之后直到程序结束,哪怕是极小内存的cudaMallocHost调用也全部失败,请问cudaFreeHost是否真的执行失败了?该如何从这种状况恢复?
补充场景:尝试分配10个2GB缓冲区,第8个分配失败,调用cudaFreeHost释放前7个时均返回cudaErrorMemoryAllocation,后续哪怕分配512KB内存也持续失败。
代码示例
#include <cuda_runtime.h> #include <iostream> #define N_ALLOCATION 10 #define SIZE (size_t)2 * (size_t)1024 * (size_t)1024 * (size_t)1024 bool MyCheck(const cudaError_t &err) { if(err == cudaSuccess) return true; std::cout << "Error " << err << std::endl; return false; } int main() { void* ptr[N_ALLOCATION] = {nullptr}; for(int i = 0; i < N_ALLOCATION; ++i) { std::cout << "allocation " << i << std::endl; if(!MyCheck(cudaMallocHost(&(ptr[i]), SIZE))) break; } for(int i = 0; i < N_ALLOCATION; ++i) { if(ptr[i]) { std::cout << "free " << i << std::endl; if(MyCheck(cudaFreeHost(ptr[i]))) { ptr[i] = nullptr; } } } std::cout << "allocation after free" << std::endl; MyCheck(cudaMallocHost(&(ptr[0]), SIZE)); }
运行输出
allocation 0 allocation 1 allocation 2 allocation 3 allocation 4 allocation 5 allocation 6 allocation 7 Error 2 free 0 free 1 Error 2 free 2 Error 2 free 3 Error 2 free 4 Error 2 free 5 Error 2 free 6 Error 2 allocation after free Error 2
我的预期:释放操作和释放后的内存分配都不应返回错误。
解答
1. cudaFreeHost是否执行失败?
没有。CUDA runtime的错误码是累积返回的——当之前的API调用(比如那次失败的cudaMallocHost)产生了cudaErrorMemoryAllocation错误后,后续所有CUDA API调用都会优先返回这个未清除的错误,直到你主动清除错误状态。也就是说,cudaFreeHost实际上已经成功释放了内存,但因为之前的错误没被清除,所以返回的还是旧错误码。
2. 如何恢复内存分配能力?
需要在错误发生后,主动清除CUDA的错误状态,常用的方法有两种:
- 调用
cudaGetLastError():该函数会返回当前累积的错误码,并清除错误状态; - 调用
cudaDeviceSynchronize():同步设备时会检查所有未完成的异步操作,同时清除累积的错误状态(如果没有其他异步错误的话)。
修改你的代码,在分配失败后先清除错误,再执行释放操作,就能解决问题。比如在分配循环break后加入:
// 清除之前的内存分配错误 cudaGetLastError();
修改后的释放循环就能正常返回cudaSuccess,后续的内存分配也能正常进行。
补充说明
CUDA的错误机制是为了让开发者不会遗漏之前的错误,但在内存分配失败这类场景下,必须手动清除错误状态才能继续正常使用CUDA API。另外,cudaFreeHost本身是同步操作,只要传入的指针是有效的,就一定会完成释放,错误码只是之前累积的结果,不是释放操作本身失败。
内容的提问来源于stack exchange,提问作者Perry

