2016-09-08 2 views
2

Я пытаюсь cudaMalloc кучу указателей устройств и изящно выходит, если какой-либо из mallocs не работает. У меня есть действующий код, но раздутый, потому что мне нужно cudaFree все, что я ранее malloc'd, если один не удается. Итак, теперь мне интересно, есть ли более сжатый способ достижения этого. Очевидно, я не могу освободить то, что не было malloc'd - это определенно вызовет проблемы.Есть ли лучший/более чистый/более элегантный способ malloc и бесплатно в cuda?

Ниже приведен фрагмент кода, который я пытаюсь сделать более элегантным.

//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 массивов. Он начинает становиться уродливым - но он работает правильно.

Мысли?

+3

Используйте 'goto', Luke. – magras

+0

'goto' не помогает отслеживать список указателей, которые были выделены, против тех, у кого нет –

+0

один простой способ: добавить все предметы в вектор и бесплатно в конце –

ответ

2

Некоторые возможности:

  1. cudaDeviceReset() освободят все распределения устройств, без необходимости проходить через список указателей.

  2. Если вы намерены выйти (приложение), все распределения устройств автоматически освобождаются при завершении работы приложения. Среда выполнения cuda обнаруживает завершение процесса, связанного с контекстом устройства приложения, и стирает этот контекст в этой точке. Поэтому, если вы просто собираетесь выйти, должно быть безопасно не выполнять никаких операций cudaFree().

4
  • Вы можете обернуть их в unique_ptr с пользовательскими Deleter. (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); 
} 
+0

Может быть [этот ответ] (http://stackoverflow.com/a/19054467/5980430) поможет вам с unique_ptr –

+0

Возможно, добавьте небольшой пример демонстрации 'unique_ptr'? Имейте в виду, что это также требует компилятора C++ 11. –

2

Первоначально хранить 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); 
1

Вопрос помечено C++, так вот на C++ решение

Общая практика заключается в приобретении ресурсов в конструкторе и выпустить в деструкторе. Идея состоит в том, что в любом случае ресурс гарантированно освобождается вызовом деструктора. Аккуратный побочный эффект заключается в том, что деструктор называется автоматически в конце области действия, поэтому вам не нужно ничего делать, чтобы ресурс был выпущен, когда он больше не используется. См. RAII

В роли ресурса могут быть разные типы памяти, дескрипторы файлов, сокеты и т. Д. Память устройств CUDA не является исключением из этого общего правила.

Я бы также отговорил вас от написания собственных классов владения ресурсами и советовал бы использовать библиотеку. thrust::device_vector, вероятно, является наиболее широко используемым контейнером памяти устройства. Библиотека Thrust является частью инструментария CUDA.

0

Да. Если вы используете (мой) 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); 

Заметим, однако, что вы могли бы просто выделить один раз и просто поместить другие указатели на соответствующее смещение.