使用unique_ptr和cudaMalloc

时间:2017-11-20 00:04:23

标签: c++11 pointers cuda unique-ptr

我一直在考虑在CUDA中使用std :: unique_ptr和设备指针。我想知道的是,当前的c ++ 11 unique_ptr是否可以与cudaMalloc一起使用。我知道它可以与普通的malloc(Is it possible to use a C++ smart pointers together with C's malloc?)一起使用,但是cudaMalloc不会在函数的return语句中返回指针。相反,它返回错误代码。指针在引用中返回。

blog post推荐以下技术:

auto deleter=[&](float* ptr){ cudaFree(ptr); };
std::unique_ptr<float[], decltype(deleter)> d_in(new float[size], deleter);
cudaMalloc((void **) &d_in, size * sizeof(float));

问题:但是,我担心这会创建永远不会被删除的主机内存(即d_in(new float[size], deleter);)?除非new float[size]实际上没有生成主机内存或被覆盖?如果以上实际上不起作用,可以定义我自己的cudaMalloc包装工作吗? - 将指针传递给unique_ptr?

类似的东西:

void* myCudaMalloc(size_t mySize){ 
    void * p; 
    checkCUDAerrorMacro(cudaMalloc((void**) &p, size);) 
    return p;
}

...

auto deleter=[](float* ptr){ cudaFree(ptr); };
std::unique_ptr<float[], decltype(deleter)> d_in(myCudaMalloc(size_t mySize), deleter); 

2 个答案:

答案 0 :(得分:7)

经过一些工作,我想出了如何测试它的3个版本 - tl;博士文章的版本(v1)确实泄漏,但可以调整,以便它不会(v2)并改进(第3节):

常用代码:

template <typename Deleter>
using unique_p = std::unique_ptr<float[], Deleter>;

constexpr int length = 20;

v1 :(博客文章推荐的内容)

void version1(){
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted1\n"; };
    unique_p<decltype(deleter)> d_in(new float[length],deleter);
    cudaMalloc((void **) &d_in, length * sizeof(float));

    ...
}

v2 :(与上面类似,但用nullptr初始化d_in)

void version2(){
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted2\n"; };
    unique_p<decltype(deleter)> d_in(nullptr,deleter);
    cudaMalloc((void **) &d_in, length * sizeof(float));

    ...
} 

v3 :( d_in&#34;采用&#34;用cudaMalloc初始化的指针)

void version3(){
    auto  myCudaMalloc = [](size_t mySize) { void* ptr; cudaMalloc((void**)&ptr, mySize); return ptr; };
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted3\n"; };
    unique_p<decltype(deleter)> d_in((float*)myCudaMalloc(length*sizeof(float)),deleter);

    ...
}

所有3个都创建适当的设备指针。但是,版本1肯定会泄漏主机内存(使用valgrind测试并禁用cuda警告:Valgrind and CUDA: Are reported leaks real?)。 v2和v3都没有泄漏主机内存。 cuda-memcheck还证实,任何版本都没有设备端内存泄漏。

在版本2和版本3之间,我更喜欢版本3,因为它更清楚unique_ptr拥有指针,它遵循unique_ptr构造函数中newmalloc的惯用法。您也只需要定义构造函数/ lambda一次,然后可以反复使用它,因此代码行数较少。

========================

完整测试代码(使用nvcc -std = c ++ 14编译):

#include <cuda_runtime.h>
#include <memory>
#include <iostream>

template <typename Deleter>
using unique_p = std::unique_ptr<float[], Deleter>;

__global__ void printArray(float * d_in, int num){
    for(int i = 0; i < num; i++){ printf("%f\t",d_in[i]); }
    printf("\n");

}

struct myDeleter{
    void operator()(float* ptr){ cudaFree(ptr); std::cout<<"\nDeleted\n"; } 
};

constexpr int length = 20;

void version1(){
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted1\n"; };
    unique_p<decltype(deleter)> d_in(new float[length],deleter);
    cudaMalloc((void **) &d_in, length * sizeof(float));

    std::unique_ptr<float[]> h_out(new float[length]);

    for(int i = 0; i < length; i++){ h_out[i] = i; }

    cudaMemcpy(d_in.get(), h_out.get(),length*sizeof(float),cudaMemcpyHostToDevice);


    printArray<<<1,1>>>(d_in.get(),length);
}

void version2(){
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted2\n"; };
    unique_p<decltype(deleter)> d_in(nullptr,deleter);
    cudaMalloc((void **) &d_in, length * sizeof(float));

    std::unique_ptr<float[]> h_out(new float[length]);

    for(int i = 0; i < length; i++){ h_out[i] = i; }

    cudaMemcpy(d_in.get(), h_out.get(),length*sizeof(float),cudaMemcpyHostToDevice);


    printArray<<<1,1>>>(d_in.get(),length);
}


void version3(){
    auto  myCudaMalloc = [](size_t mySize) { void* ptr; cudaMalloc((void**)&ptr, mySize); return ptr; };
    auto deleter = [](float* ptr) { cudaFree(ptr); std::cout<<"\nDeleted3\n"; };
    unique_p<decltype(deleter)> d_in((float*)myCudaMalloc(length*sizeof(float)),deleter);
    //unique_p<myDeleter> d_in((float*)myCudaMalloc(20*sizeof(float)));

    std::unique_ptr<float[]> h_out(new float[length]);
    for(int i = 0; i < length; i++){ h_out[i] = i; }

    cudaMemcpy(d_in.get(), h_out.get(),length*sizeof(float),cudaMemcpyHostToDevice);

    printArray<<<1,1>>>(d_in.get(),length);
}

int main(){

    version1();
    version2();
    version3();

    cudaDeviceReset();
    return 0;
}

答案 1 :(得分:0)

这种模式对我很有效:

int main(){
float* deviceArray_raw;
gpuErrchk(cudaMalloc((void**)&deviceArray_raw, 100 * sizeof(float)));
auto deleter = [](float* ptr) { gpuErrchk(cudaFree(ptr)); };
std::unique_ptr<float[], decltype(deleter)> deviceArray(deviceArray_raw, deleter);
...
...
return 0;
}

除了主机内存泄漏之外,还需要注意设备内存泄漏。 在 gpuErrchk 中结束 cuda API 调用有助于解决这个问题。使用它,我能够捕捉到一些奇怪的行为。