2017-04-19 4 views
0

CUDAでのstd ::ベクトルを使用する方法が明らかにされていませんが、私は私自身のVectorクラスを設計した:ベクトルへの対処 - cudaMemcpyDeviceToHost

#ifndef VECTORHEADERDEF 
#define VECTORHEADERDEF 

#include <cmath> 
#include <iostream> 
#include <cassert> 

template <typename T> 
class Vector 
{ 
private: 
    T* mData; // data stored in vector 
    int mSize; // size of vector 
public: 
     Vector(const Vector& otherVector); // Constructor 
     Vector(int size); // Constructor 
     ~Vector(); // Desructor 

     __host__ __device__ int GetSize() const; // get size of the vector 

     T& operator[](int i); // see element 

     // change element i 
     __host__ __device__ void set(size_t i, T value) { 
       mData[i] = value; 
     } 

     template <class S> // output vector 
     friend std::ostream& operator<<(std::ostream& output, Vector<S>& v); 
}; 


// Overridden copy constructor 
// Allocates memory for new vector, and copies entries of other vector into it 
template <typename T> 
Vector<T>::Vector(const Vector& otherVector) 
{ 
    mSize = otherVector.GetSize(); 
    mData = new T [mSize]; 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = otherVector.mData[i]; 
    } 
} 

// Constructor for vector of a given size 
// Allocates memory, and initialises entries to zero 
template <typename T> 
Vector<T>::Vector(int size) 
{ 
    assert(size > 0); 
    mSize = size; 
    mData = new T [mSize]; 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = 0.0; 
    } 
} 

// Overridden destructor to correctly free memory 
template <typename T> 
Vector<T>::~Vector() 
{ 
    delete[] mData; 
} 

// Method to get the size of a vector 
template <typename T> 
__host__ __device__ int Vector<T>::GetSize() const 
{ 
    return mSize; 
} 

// Overloading square brackets 
// Note that this uses `zero-based' indexing, and a check on the validity of the index 
template <typename T> 
T& Vector<T>::operator[](int i) 
{ 
     assert(i > -1); 
     assert(i < mSize); 
     return mData[i]; 
} 

// Overloading the assignment operator 
template <typename T> 
Vector<T>& Vector<T>::operator=(const Vector& otherVector) 
{ 
    assert(mSize == otherVector.mSize); 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = otherVector.mData[i]; 
    } 
    return *this; 
} 

// Overloading the insertion << operator 
template <typename T> 
std::ostream& operator<<(std::ostream& output, Vector<T>& v) { 
    for (int i=0; i<v.mSize; i++) { 
     output << v[i] << " "; 
    } 
    return output; 
} 

私の主な機能 - 私はちょうどにベクトルを渡しますデバイス、それを変更し、それをバックパス - 次のように(単にテスト目的のために設計されたカーネルで)です:

#include <iostream> 

#include "Vector.hpp" 


__global__ void alpha(Vector<int>* d_num) 
{ 
     int myId = threadIdx.x + blockDim.x * blockIdx.x; 


     d_num->set(0,100); 
     d_num->set(2,11); 
} 


int main() 
{ 
     Vector<int> num(10); 

     for (int i=0; i < num.GetSize(); ++i) num.set(i,i); // initialize elements to 0:9 

     std::cout << "Size of vector: " << num.GetSize() << "\n"; 
     std::cout << num << "\n"; // print vector 

     Vector<int>* d_num; 

     // allocate global memory on the device 
     cudaMalloc((void **) &d_num, num.GetSize()*sizeof(int)); 

     // copy data from host memory to the device memory 
     cudaMemcpy(d_num, &num[0], num.GetSize()*sizeof(int), cudaMemcpyHostToDevice); 


     // launch the kernel 
     alpha<<<1,100>>>(d_num); 


     // copy the modified array back to the host, overwriting the contents of h_arr 
     cudaMemcpy(num, &d_num[0], num.GetSize()*sizeof(int), cudaMemcpyDeviceToHost); 

     std::cout << num << "\n"; 


     // free GPU memory allocation and exit 
     cudaFree(d_num); 

     return 0; 
} 

私が遭遇した問題はcudaMemcpyDeviceToHostです。実際には、出力から見られるように、デバイスベクトルをnumベクトルにコピーしません。

どうすれば対応できますか? (明示してください、私はCUDAをかなり新しくしています)。これはないだろう

cudaMemcpy(d_num, &num[0], num.GetSize()*sizeof(int), cudaMemcpyHostToDevice); 
         ^^^^^^^ 

:あなたのVectorオブジェクトの名前は、そのへのポインタではありません

cudaMemcpy(num, &d_num[0], num.GetSize()*sizeof(int), cudaMemcpyDeviceToHost); 
       ^^^ 

答えて

1

これは、ベクトルnumの最初の要素への有効なポインタを作成します。最初のデータ要素。代わりに、次のように、あなたが書いた最初のものと同様の方法でその行を書くべきです:

cudaMemcpy(&num[0], d_num, num.GetSize()*sizeof(int), cudaMemcpyDeviceToHost); 

しかし、これだけでは修正されていません。 d_numVectorではありませんが、既にポインタであるため、これらの操作で直接使用できます。 &(d_num[0])を使用するのは間違いではありませんが、そうする必要はありません。

d_numは(あなたがそれを割り当てたとして - それはint量のセットに裸のポインタである)Vectorないので、カーネル内Vectorメソッドの使用状況も壊れています。カーネルでVectorメソッドを使用する場合は、データだけでなく、実際のVectorオブジェクトを渡す必要があります。オブジェクトを渡すと、オブジェクト内でデバイスのデータ処理が必要になります(ホスト上でアクセス可能なデータはデバイス上でアクセスできず、その逆もあります)。Vectorクラスの広範囲な書き換えです。私はその試みを制限して、可能な方法を示しています。

    オブジェクトには、データのホストコピーとデータのデバイスコピーの両方へのポインタが含まれています。
  1. オブジェクトのインスタンス化では、両方を割り当て、最初に "参照"ポインタをホストコピーを指すように設定します。
  2. デバイスで使用する前に、デバイスデータにホストデータをコピーし、この目的でto_device()メソッドを使用する必要があります。この方法では、Vectorデータのデバイス側のコピーを参照するように、「参照」ポインタ(mData)も切り替えます。
  3. ホストデータをオブジェクトの「内部」のデバイスデータにコピーすることに加えて、オブジェクト自体をデバイスで使用できるようにする必要があります。このために、オブジェクト側のコピー(d_num)へのポインタを介してオブジェクト自体をコピーします。
  4. __device__デコレーションのメソッドの場合、デバイス上で通常の方法でオブジェクトを使用できます。
  5. カーネルの完了後、データのホストコピーを更新し、 "参照"ポインタをホストデータに戻す必要があります。この目的のためにto_host()方法が提供される。
  6. その後、カーネル内で発生したデータがあれば、データ変更を反映してオブジェクトをホストコードで再度使用することができます。ここで

加工した例である:

$ cat t101.cu 
#include <iostream> 

#include <cmath> 
#include <iostream> 
#include <cassert> 

template <typename T> 
class Vector 
{ 
private: 
    T* mData, *hData, *dData; // data stored in vector 

    int mSize; // size of vector 
public: 
     Vector(const Vector& otherVector); // Constructor 
     Vector(int size); // Constructor 
     ~Vector(); // Desructor 

     __host__ __device__ int GetSize() const; // get size of the vector 
     __host__ __device__ T& operator[](int i); // see element 

     // change element i 
     __host__ __device__ void set(size_t i, T value) { 
       mData[i] = value; 
     }; 

     __host__ __device__ Vector<T>& operator=(const Vector<T>& otherVector); 
     void to_device(); 
     void to_host(); 
     template <class S> // output vector 
     friend std::ostream& operator<<(std::ostream& output, Vector<S>& v); 
}; 


// Overridden copy constructor 
// Allocates memory for new vector, and copies entries of other vector into it 
template <typename T> 
Vector<T>::Vector(const Vector& otherVector) 
{ 
    mSize = otherVector.GetSize(); 
    hData = new T [mSize]; 
    cudaMalloc(&dData, mSize*sizeof(T)); 
    mData = hData; 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = otherVector.mData[i]; 
    } 
} 

// Constructor for vector of a given size 
// Allocates memory, and initialises entries to zero 
template <typename T> 
Vector<T>::Vector(int size) 
{ 
    assert(size > 0); 
    mSize = size; 
    hData = new T [mSize]; 
    cudaMalloc(&dData, mSize*sizeof(T)); 
    mData = hData; 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = 0.0; 
    } 
} 

// Overridden destructor to correctly free memory 
template <typename T> 
Vector<T>::~Vector() 
{ 
    delete[] hData; 
    if (dData) cudaFree(dData); 
} 

// Method to get the size of a vector 
template <typename T> 
__host__ __device__ 
int Vector<T>::GetSize() const 
{ 
    return mSize; 
} 

// Overloading square brackets 
// Note that this uses `zero-based' indexing, and a check on the validity of the index 
template <typename T> 
__host__ __device__ 
T& Vector<T>::operator[](int i) 
{ 
     assert(i > -1); 
     assert(i < mSize); 
     return mData[i]; 
} 

// Overloading the assignment operator 
template <typename T> 
__host__ __device__ 
Vector<T>& Vector<T>::operator=(const Vector<T>& otherVector) 
{ 
    assert(mSize == otherVector.mSize); 
    for (int i=0; i<mSize; i++) 
    { 
     mData[i] = otherVector.mData[i]; 
    } 
    return *this; 
} 

// Overloading the insertion << operator 
// not callable on the device! 
template <typename T> 
std::ostream& operator<<(std::ostream& output, Vector<T>& v) { 
    for (int i=0; i<v.mSize; i++) { 
     output << v[i] << " "; 
    } 
    return output; 
} 

template <typename T> 
void Vector<T>::to_device(){ 
    cudaMemcpy(dData, hData, mSize*sizeof(T), cudaMemcpyHostToDevice); 
    mData = dData; 
} 

template <typename T> 
void Vector<T>::to_host(){ 
    cudaMemcpy(hData, dData, mSize*sizeof(T), cudaMemcpyDeviceToHost); 
    mData = hData; 
} 

__global__ void alpha(Vector<int> *d_num) 
{ 


     d_num->set(0,100); 
     d_num->set(2,11); 
     (*d_num)[1] = 50; 
} 


int main() 
{ 
     Vector<int> num(10); 

     for (int i=0; i < num.GetSize(); ++i) num.set(i,i); // initialize elements to 0:9 

     std::cout << "Size of vector: " << num.GetSize() << "\n"; 
     std::cout << num << "\n"; // print vector 

     Vector<int> *d_num; 
     cudaMalloc(&d_num, sizeof(Vector<int>)); 

     num.to_device(); 
     cudaMemcpy(d_num, &(num), sizeof(Vector<int>), cudaMemcpyHostToDevice); 
     // launch the kernel 
     alpha<<<1,1>>>(d_num); 


     // copy the modified array back to the host, overwriting the contents of h_arr 
     num.to_host(); 

     std::cout << num << "\n"; 


     // free GPU memory allocation and exit 

     return 0; 
} 
$ nvcc -arch=sm_61 -o t101 t101.cu 
$ cuda-memcheck ./t101 
========= CUDA-MEMCHECK 
Size of vector: 10 
0 1 2 3 4 5 6 7 8 9 
100 50 11 3 4 5 6 7 8 9 
========= ERROR SUMMARY: 0 errors 
$ 

注:

  1. 私はあなたのVectorにその他の変更をしなければならなかったので、私のテストによると、あなたの投稿のコードでは、さまざまなコンパイルエラーが発生しましたクラスをコンパイルするだけです。

  2. オブジェクトを値でカーネルに渡すと、コピーコンストラクタが呼び出され、その後はデストラクタが呼び出されて処理が難しくなるため、オブジェクトをポインタで渡すことにしました。これを避けるために。

  3. あなたのカーネルコールは100スレッドを起動しています。彼らはまったく同じことをやっているので、読んでいることがなくても特に問題はありませんが、単なるスレッドに変更しました。それでも、同じ機能を実証しています。

+0

ありがとう、ロバート。あなたの答えはとてもいいです。カーネルは単なる実験的なものでした。 推力ベクトルは同様の設計ですか? 型の変数を宣言して(デバイスに渡すことはできますか?)Vector ここで、Objectはプライベートメンバーとして別のベクタを持つクラスですか? –

+0

推力ベクトルには同様の設計はありません。推力には、ホストベクトルと別のデバイスベクトルクラスがあります。ホストとデバイスのストレージを1つのクラスにまとめません。しかし、デザインは(IMO)かなりきれいで、明らかにあなたがここにあるよりもはるかに洗練されています。推力ベクトルの詳細については、推力[クイックスタートガイド](https://github.com/thrust/thrust/wiki/Quick-Start-Guide)をお試しください。また、推力はオープンソースです。Nietherここの方法や推力(デバイス)ベクトルは簡単にベクトルのベクトルを扱うことができますが、より単純なオブジェクトのベクトルも可能です。 –

0

推力はCUDAのために書かれたライブラリであり、ベクトルを持っています。 http://docs.nvidia.com/cuda/thrust/ 多分それはあなたが必要としないならば、あなたが必要とするすべての機能を持っています。

+0

マックス、理由は、私はベクトルを持っていると思います。タイプはベクトルを含むオブジェクトでもありますし、このオブジェクト内のベクトルをどのように宣言すればいいですか? ? 最後に、この質問はより一般的なものと見なすことができます。 –

1

あなたが問題を抱えているのはcudaMemcpyDeviceToHostの部分だけではありません。

Vector<int> num(10); 
Vector<int>* d_num; 
cudaMalloc(&d_num, num.GetSize()*sizeof(int)); 

これはタイプVector<int>*d_numによって指し示されるCUDAグローバルメモリ(sizeof(int)が4であると仮定して)、上に40のバイトを割り当てます。私はVector<int>オブジェクト自体が40バイトであるとは思わないと思います。

別の方法を試してみましょう。

cudaMalloc(&d_num, sizeof(Vector<int>)); 
cudaMalloc(&d_num->mData, num.GetSize()*sizeof(int)); // assume mData is a public attribute 

あなたがホストコード(d_num->mData)からデバイスメモリにアクセスしているので、残念ながら、2行目はsegmentation faultを放出します。

したがって、Vectorクラスの実装には多くの誤りがあります。固定サイズの配列を作成する場合は、d_numをポインタとして宣言するだけです。

int* d_num; 
cudaMalloc(&d_num, num.GetSize()*sizeof(int)); 
cudaMemcpy(d_num, &num[0], num.GetSize()*sizeof(int), cudaMemcpyHostToDevice); 
// .. some kernel operations 
cudaMemcpy(&num[0], d_num, num.GetSize()*sizeof(int), cudaMemcpyDeviceToHost); 
関連する問題