いくつかの作業の後、私はそれの3つのバージョンをテストする方法を考え出した - TL; DRのブログ記事のバージョン(V1)がリーク確かにありませんが、それはdoesnのように微調整することができます「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:
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(上記と同様、しかしnullptrとD_IN初期化):(cudaMallocで初期化ポインタを "採用" D_IN)
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の間では、unique_ptrがポインターを所有していることを明確にし、unique_ptrコンストラクターのnew
とmalloc
のイディオムに従います。また、構築関数/ 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;
}