2017-11-20 9 views
0

나는 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 문에서 포인터를 반환하지 않습니다. 대신 오류 코드를 반환합니다. 포인터가 참조로 반환됩니다.unique_ptr과 cudaMalloc의 사용

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); 

답변

1

몇 가지 작업 후 나는 그것의 3 버전을 테스트하는 방법을 알아 냈 - TL; DR 블로그 게시물의 버전 (V1)가 누출 실제로 수행하지만 아무튼 있도록 불통 될 수있다 't (V2) 및 개선 (V3) :

공통 코드 :

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 :

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 적절한 장치 포인터 생성 (cudaMalloc로 초기화 포인터 "채용"d_in) . 그러나 버전 1에서는 확실히 호스트 메모리가 누수됩니다 (cuda 경고가 표시되지 않는 valgrind를 사용하여 테스트 됨 : Valgrind and CUDA: Are reported leaks real?). v2와 v3 모두 호스트 메모리를 유출하지 않습니다. cuda-memcheck는 또한 모든 버전에서 장치 측 메모리 누수가 없음을 확인했습니다.

버전 2와 3 사이에서 unique_ptr이 포인터를 소유한다는 것을 명확히하고 unique_ptr 생성자에서 newmalloc이라는 관용구를 따르므로 버전 3을 선호합니다. 또한/λ를 생성하는 함수를 한 번 정의한 다음 여러 번 반복하여 사용할 수 있으므로 코드 줄이 줄었습니다.

:

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

전체 테스트 코드 (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; 
}