在 cuda 中是否有 better/cleaner/more 优雅的 malloc 和 free 方法?
Is there a better/cleaner/more elegant way to malloc and free in cuda?
我正在尝试 cudaMalloc 一堆设备指针,并在任何 mallocs 不起作用时优雅地退出。我有功能代码 - 但臃肿,因为如果一个失败,我必须 cudaFree 我以前 malloc'd 的一切。所以现在我想知道是否有更简洁的方法来完成这个。显然我不能释放没有被 malloc 的东西——那肯定会导致问题。
下面是我试图使其更优雅的代码片段。
//define device pointers
float d_norm, *d_dut, *d_stdt, *d_gamma, *d_zeta;
//allocate space on the device for the vectors and answer
if (cudaMalloc(&d_norm, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
return;
};
if (cudaMalloc(&d_data, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
return;
};
if (cudaMalloc(&d_stdt, sizeof(float)*wSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_data);
return;
};
if (cudaMalloc(&d_gamma, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_dut);
cudaFree(d_stdt);
return;
};
if (cudaMalloc(&d_zeta, sizeof(float)*w) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_dut);
cudaFree(d_stdt);
cudaFree(d_gamma);
return;
};
这是一个简化版本,但您可以看到它是如何不断构建的。实际上,我正在尝试 malloc 大约 15 个数组。它开始变得丑陋 - 但它工作正常。
想法?
一些可能性:
cudaDeviceReset()
将释放所有设备分配,您无需 运行 通过指针列表。
如果您打算退出(应用程序),无论如何,所有设备分配都会在应用程序终止时自动释放。 cuda 运行time 检测与应用程序的设备上下文关联的进程的终止,并在此时擦除该上下文。因此,如果您只是要退出,不执行任何 cudaFree()
操作应该是安全的。
您可以使用自定义删除器将它们包装到 unique_ptr 中。 (c++11)
或者当成功分配并释放向量中的所有指针时,只添加到一个向量。
关于 unique_ptr 的示例:
#include <iostream>
#include <memory>
using namespace std;
void nativeFree(float* p);
float* nativeAlloc(float value);
class NativePointerDeleter{
public:
void operator()(float* p)const{nativeFree(p);}
};
int main(){
using pointer_type = unique_ptr<float,decltype(&nativeFree)>;
using pointer_type_2 = unique_ptr<float,NativePointerDeleter>;
pointer_type ptr(nativeAlloc(1),nativeFree);
if(!ptr)return 0;
pointer_type_2 ptr2(nativeAlloc(2));//no need to provide deleter
if(!ptr2)return 0;
pointer_type ptr3(nullptr,nativeFree);//simulate a fail alloc
if(!ptr3)return 0;
/*Do Some Work*/
//now one can return without care about all the pointers
return 0;
}
void nativeFree(float* p){
cout << "release " << *p << '\n';
delete p;
}
float* nativeAlloc(float value){
return new float(value);
}
最初将nullptr
存储在所有指针中。 free
对空指针无效。
int* p1 = nullptr;
int* p2 = nullptr;
int* p3 = nullptr;
if (!(p1 = allocate()))
goto EXIT_BLOCK;
if (!(p2 = allocate()))
goto EXIT_BLOCK;
if (!(p3 = allocate()))
goto EXIT_BLOCK;
EXIT_BLOCK:
free(p3); free(p2); free(p1);
问题被标记为 C++,所以这是一个 C++ 解决方案
一般的做法是在构造函数中获取资源,在析构函数中释放。这个想法是在任何情况下都保证通过调用析构函数来释放资源。整洁的副作用是析构函数在范围的末尾自动调用,因此您根本不需要做任何事情来释放不再使用的资源。参见 RAII
在资源角色中,可能有各种内存类型、文件句柄、套接字等。CUDA 设备内存也不例外。
我也不鼓励您编写自己拥有的资源 类,并建议您使用图书馆。 thrust::device_vector
可能是使用最广泛的设备内存容器。 Thrust 库是 CUDA 工具包的一部分。
是的。如果你使用 (my) CUDA Modern-C++ API wrapper library,你可以只使用独特的指针,当它们的生命周期结束时,它们将被释放。您的代码将仅变为以下内容:
auto current_device = cuda::device::current::get();
auto d_dut = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_stdt = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_gamma = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_zeta = cuda::memory::device::make_unique<float[]>(current_device, vSize);
不过请注意,您可以只分配一次并将其他指针放在适当的偏移量处。
我正在尝试 cudaMalloc 一堆设备指针,并在任何 mallocs 不起作用时优雅地退出。我有功能代码 - 但臃肿,因为如果一个失败,我必须 cudaFree 我以前 malloc'd 的一切。所以现在我想知道是否有更简洁的方法来完成这个。显然我不能释放没有被 malloc 的东西——那肯定会导致问题。
下面是我试图使其更优雅的代码片段。
//define device pointers
float d_norm, *d_dut, *d_stdt, *d_gamma, *d_zeta;
//allocate space on the device for the vectors and answer
if (cudaMalloc(&d_norm, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
return;
};
if (cudaMalloc(&d_data, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
return;
};
if (cudaMalloc(&d_stdt, sizeof(float)*wSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_data);
return;
};
if (cudaMalloc(&d_gamma, sizeof(float)*vSize) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_dut);
cudaFree(d_stdt);
return;
};
if (cudaMalloc(&d_zeta, sizeof(float)*w) != cudaSuccess) {
std::cout << "failed malloc";
cudaFree(d_norm);
cudaFree(d_dut);
cudaFree(d_stdt);
cudaFree(d_gamma);
return;
};
这是一个简化版本,但您可以看到它是如何不断构建的。实际上,我正在尝试 malloc 大约 15 个数组。它开始变得丑陋 - 但它工作正常。
想法?
一些可能性:
cudaDeviceReset()
将释放所有设备分配,您无需 运行 通过指针列表。如果您打算退出(应用程序),无论如何,所有设备分配都会在应用程序终止时自动释放。 cuda 运行time 检测与应用程序的设备上下文关联的进程的终止,并在此时擦除该上下文。因此,如果您只是要退出,不执行任何
cudaFree()
操作应该是安全的。
您可以使用自定义删除器将它们包装到 unique_ptr 中。 (c++11)
或者当成功分配并释放向量中的所有指针时,只添加到一个向量。
关于 unique_ptr 的示例:
#include <iostream>
#include <memory>
using namespace std;
void nativeFree(float* p);
float* nativeAlloc(float value);
class NativePointerDeleter{
public:
void operator()(float* p)const{nativeFree(p);}
};
int main(){
using pointer_type = unique_ptr<float,decltype(&nativeFree)>;
using pointer_type_2 = unique_ptr<float,NativePointerDeleter>;
pointer_type ptr(nativeAlloc(1),nativeFree);
if(!ptr)return 0;
pointer_type_2 ptr2(nativeAlloc(2));//no need to provide deleter
if(!ptr2)return 0;
pointer_type ptr3(nullptr,nativeFree);//simulate a fail alloc
if(!ptr3)return 0;
/*Do Some Work*/
//now one can return without care about all the pointers
return 0;
}
void nativeFree(float* p){
cout << "release " << *p << '\n';
delete p;
}
float* nativeAlloc(float value){
return new float(value);
}
最初将nullptr
存储在所有指针中。 free
对空指针无效。
int* p1 = nullptr;
int* p2 = nullptr;
int* p3 = nullptr;
if (!(p1 = allocate()))
goto EXIT_BLOCK;
if (!(p2 = allocate()))
goto EXIT_BLOCK;
if (!(p3 = allocate()))
goto EXIT_BLOCK;
EXIT_BLOCK:
free(p3); free(p2); free(p1);
问题被标记为 C++,所以这是一个 C++ 解决方案
一般的做法是在构造函数中获取资源,在析构函数中释放。这个想法是在任何情况下都保证通过调用析构函数来释放资源。整洁的副作用是析构函数在范围的末尾自动调用,因此您根本不需要做任何事情来释放不再使用的资源。参见 RAII
在资源角色中,可能有各种内存类型、文件句柄、套接字等。CUDA 设备内存也不例外。
我也不鼓励您编写自己拥有的资源 类,并建议您使用图书馆。 thrust::device_vector
可能是使用最广泛的设备内存容器。 Thrust 库是 CUDA 工具包的一部分。
是的。如果你使用 (my) CUDA Modern-C++ API wrapper library,你可以只使用独特的指针,当它们的生命周期结束时,它们将被释放。您的代码将仅变为以下内容:
auto current_device = cuda::device::current::get();
auto d_dut = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_stdt = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_gamma = cuda::memory::device::make_unique<float[]>(current_device, vSize);
auto d_zeta = cuda::memory::device::make_unique<float[]>(current_device, vSize);
不过请注意,您可以只分配一次并将其他指针放在适当的偏移量处。