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

Сейчас я начинаю использовать CUDA и должен признать, что немного разочарован C API. Я понимаю причины выбора C, но если бы язык основывался на 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 (которая, как я слышал, улучшит поддержку C++, что бы они ни подразумевали под этим).

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

  1. Мое место размещения new перегрузить семантически правильно? Это утечка памяти?
  2. Кто-нибудь имеет информацию о будущих разработках CUDA, которые идут в этом общем направлении (давайте посмотрим правде в глаза: интерфейсы C в C++ s*ck)?
  3. Как я могу продолжать это согласованным образом (есть другие 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&);
};

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

4 ответа

Решение

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

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

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

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

Кто-нибудь имеет информацию о будущих разработках CUDA, которые идут в этом общем направлении (давайте посмотрим правде в глаза: интерфейсы C в C++ s*ck)?

Да, я сделал что-то подобное:

https://github.com/eyalroz/cuda-api-wrappers/

Runtime API nVIDIA для CUDA предназначен для использования как в коде C, так и в C++. Таким образом, он использует API в стиле C, нижний общий знаменатель (с несколькими заметными исключениями перегрузок шаблонных функций).

Эта библиотека оберток вокруг Runtime API предназначена для того, чтобы позволить нам охватить многие функции C++ (включая некоторые C++11) для использования API времени выполнения - но без снижения выразительности или повышения уровня абстракции (как, например, например, Тяги). Используя cuda-api-wrappers, у вас все еще есть свои устройства, потоки, события и т. Д., Но с ними будет удобнее работать в C++ - идиоматическими способами.

Есть несколько проектов, которые пытаются что-то подобное, например, CUDPP.

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

Другие вопросы по тегам