CUDA:C ++でのデバイスメモリ割り当てのラッピング
-
08-07-2019 - |
質問
現在CUDAを使用し始めており、C APIに少しがっかりしていることを認めなければなりません。私はCを選択する理由を理解していますが、言語が代わりにC ++に基づいていたなら、いくつかの側面はもっと簡単だったでしょう。デバイスのメモリ割り当て(cudaMalloc
経由)。
私の計画では、オーバーロードされたoperator new
を配置new
とRAII(2つの選択肢)で使用して、これを自分で行うことでした。今のところ気付いていない警告があるかどうか疑問に思っています。コードは動作しているようです ですが、メモリリークの可能性についてはまだ疑問に思っています。
RAII コードの使用法は次のとおりです。
CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.
おそらく、このコンテキストではクラスが過剰である(特に、RAIIをカプセル化するクラスであるcudaMemcpy
を使用する必要があるため)ので、他のアプローチは placement cudaDevice
:
float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);
ここで、<=>は単にオーバーロードをトリガーするタグとして機能します。ただし、通常の配置では<=>これが配置を示すため、クラスを使用するよりも構文が奇妙に一貫しており、おそらく望ましいと思われます。
あらゆる種類の批判に感謝します。おそらく誰かがこの方向の何かがCUDAの次のバージョンで計画されているかどうか知っていますか(聞いたように、それが意味するものは何でもC ++サポートを改善します)。
つまり、私の質問は実際には3つです。
- 私の配置<=>オーバーロードは意味的に正しいですか?メモリをリークしますか?
- この一般的な方向に進む将来のCUDA開発についての情報を持っている人はいますか(直面してみましょう:C ++ s * ckのCインターフェイス)
- 一貫性のある方法でこれをさらに進めるにはどうすればよいですか(他にも考慮すべき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&);
};
ここで採用されているシングルトンについて:はい、その欠点を認識しています。ただし、これらはこのコンテキストでは関係ありません。ここで必要なのは、コピーできない小さなタイプのタグだけでした。他のすべて(つまり、マルチスレッドの考慮事項、初期化の時間)は適用されません。
解決
配置の新しいアプローチを採用します。次に、std :: allocator <!> lt; <!> gt;に準拠するクラスを定義します。インタフェース。理論的には、このクラスをテンプレートパラメータとしてstd :: vector <!> lt; <!> gt;に渡すことができます。およびstd :: map <!> lt; <!> gt;などなど。
注意してください、そのようなことをするのは難しいと聞いたことがありますが、少なくともこの方法でSTLについてより多くを学ぶことになるでしょう。また、コンテナとアルゴリズムを再発明する必要はありません。
他のヒント
その間に、いくつかのさらなる開発がありました(CUDA APIに関してはそれほどではありませんが、少なくともCUDAデータ管理へのSTLのようなアプローチを試みるプロジェクトに関しては)。
最も注目すべきは、NVIDIAの調査によるプロジェクトです:スラスト
この一般的な方向に進む将来のCUDA開発についての情報を持っている人はいますか(それでは、C ++ s * ckのCインターフェイス)。
はい、私はそのようなことをしました:
https://github.com/eyalroz/cuda-api-wrappers/
nVIDIAのCUDA用ランタイムAPIは、CコードとC ++コードの両方で使用することを目的としています。そのため、C言語のAPIである低公分母を使用します(テンプレート化された関数のオーバーロードのいくつかの顕著な例外があります)。
ランタイムAPIのラッパーのこのライブラリは、ランタイムAPIを使用するためにC ++の多くの機能(一部のC ++ 11を含む)を受け入れることを目的としていますが、表現力を低下させたり、抽象化のレベルを(例えば、Thrustライブラリー)。 cuda-api-wrappersを使用すると、デバイス、ストリーム、イベントなどを引き続き使用できますが、より便利なC ++イディオマティックな方法で作業する方が便利です。
同様のことを試みるプロジェクトがいくつかあります。たとえば、 CUDPP です。
その間、私は独自のアロケータを実装しましたが、うまく機能し、簡単でした(<!> gt; 95%定型コード)。