Подтвердить что ты не робот

CUDA: распределение памяти устройства в С++

Я начинаю использовать CUDA на данный момент и должен признать, что я немного разочарован C API. Я понимаю причины выбора C, но вместо того, чтобы язык был основан на С++, несколько аспектов были бы намного проще, например. (через cudaMalloc).

Мой план состоял в том, чтобы сделать это сам, используя перегруженный operator new с размещением new и RAII (две альтернативы). Мне интересно, есть ли какие-либо предостережения, которых я пока не заметил. Код, похоже, работает, но мне все еще интересно узнать о потенциальных утечках памяти.

Использование кода RAII будет следующим:

CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.

Возможно, класс переполнен в этом контексте (тем более, что вам все равно придется использовать cudaMemcpy, класс только инкапсулирует RAII), поэтому другой подход будет размещение new:

float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);

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

Буду признателен за любую критику. Может кто-нибудь, возможно, знает, запланировано ли что-то в этом направлении для следующей версии CUDA (которая, как я слышал, улучшит ее поддержку на С++, что бы это ни значило).

Итак, мой вопрос на самом деле трижды:

  • Является ли мое размещение new перегруженным семантически правильным? Это утечка памяти?
  • Есть ли у кого-нибудь информация о будущих разработках CUDA, которые идут в этом общем направлении (давайте посмотрим на это: интерфейсы C в С++ s * ck)?
  • Как я могу сделать это дальше последовательно (есть другие API-интерфейсы для рассмотрения, например, там не только память устройства, но также постоянное хранилище и память текстур)?

// Singleton tag for CUDA device memory placement.
struct CudaDevice {
    static CudaDevice const& get() { return instance; }
private:
    static CudaDevice const instance;
    CudaDevice() { }
    CudaDevice(CudaDevice const&);
    CudaDevice& operator =(CudaDevice const&);
} const& cudaDevice = CudaDevice::get();

CudaDevice const CudaDevice::instance;

inline void* operator new [](std::size_t nbytes, CudaDevice const&) {
    void* ret;
    cudaMalloc(&ret, nbytes);
    return ret;
}

inline void operator delete [](void* p, CudaDevice const&) throw() {
    cudaFree(p);
}

template <typename T>
class CudaArray {
public:
    explicit
    CudaArray(std::size_t size) : size(size), data(new (cudaDevice) T[size]) { }

    operator T* () { return data; }

    ~CudaArray() {
        operator delete [](data, cudaDevice);
    }

private:
    std::size_t const size;
    T* const data;

    CudaArray(CudaArray const&);
    CudaArray& operator =(CudaArray const&);
};

О синглтоне, используемом здесь: Да, я знаю о его недостатках. Однако в этом контексте они не актуальны. Все, что мне было нужно, это тег небольшого типа, который не был скопирован. Все остальное (т.е. Многопоточность, время инициализации) не применяются.

4b9b3361

Ответ 1

Я бы пошел с новым подходом к размещению. Тогда я бы определил класс, который соответствует интерфейсу std:: allocator < > . Теоретически вы можете передать этот класс в качестве параметра шаблона в std::vector < > и std:: map < > и т.д.

Опасайтесь, я слышал, что делать такие вещи сопряжено с трудностями, но, по крайней мере, вы узнаете намного больше об STL таким образом. И вам не нужно повторно изобретать свои контейнеры и алгоритмы.

Ответ 2

Тем временем были некоторые дальнейшие изменения (не столько с точки зрения API CUDA, сколько по проектам, пытающимся использовать STL-подход к управлению данными CUDA).

В частности, есть проект из исследования NVIDIA: thrust

Ответ 3

Кажется, вы обвинили нас в том, что сообщаете нам, что вы планируете делать, объясняя свои варианты использования данных (это, вероятно, потому, что это очевидно для вас).

Вы имеете в виду "без"? Правильно, извините. CUDA - это язык программирования GPGPU от NVIDIA, построенный поверх C/С++, предоставляя интерфейс для GCC. Мой вопрос в первую очередь направлен на людей, которые уже знают все это. Мое использование довольно произвольно, вопрос действительно больше связан с CUDA, потому что CUDA предлагает только C-интерфейс и, таким образом, заставляет вас отказаться от множества полезных функций С++, даже если вы все равно работаете на С++.

Единственный API C, который я вижу до сих пор, - cudaMalloc и cudaFree.... Не можете ли вы просто обернуть их внутри конструктора/деструктора вашего CudoClass.

Да... и нет. Это более или менее то, что я делаю в данный момент, но я не доволен этим. Мой вопрос на самом деле трижды (я уточню вопрос):

  • Является ли мое размещение new перегруженным семантически правильным? Это утечка памяти?
  • Есть ли у кого-нибудь информация о будущих разработках CUDA, которые идут в этом общем направлении (давайте посмотрим на это: интерфейсы C в С++ s * ck)?
  • Как я могу сделать это дальше последовательно (есть другие API-интерфейсы для рассмотрения, например, там не только память устройства, но также постоянное хранилище и память текстур)?

Помимо Malloc и Free, какой другой API существует? Предполагаю, что они распределяют память и копируют данные с устройства в недавно выделенную память?

Да... на самом деле, я просто подумал о способе инкапсуляции функции cudaMemcpy.; -)

Вы хотите просмотреть необработанные данные в виде массивов определенного типа? Или другие операции, которые вы хотите выполнить?

На самом деле, как только память инициализируется, и некоторые данные копируются в нее (см. cudaMemcpy выше), я в значительной степени выполняю. Остальная часть действия принимает участие в GPU, где мне нужны только некоторые основные обращения к массиву. Самый основной рабочий процесс:

  • Выделить память устройства,
  • Скопируйте данные в память устройства,
  • Вызов (параллельного) действия GPU, обрабатывающего память,
  • Скопировать данные обратно в оперативную память.

Шаг 3 в значительной степени установлен в камне.

Ответ 4

Уже есть два проекта, которые пытаются что-то подобное:

Тем не менее, тем не менее, я реализовал свой распределитель, и он работает очень хорошо и полностью исчерпан ( > 95% -ный шаблонный код).