16

私のコードでは、関数への引数の受け渡しを容易にするために構造体を使用しています (構造体の配列は使用しませんが、一般的には配列の構造体を使用します)。私がcuda-gdbにいて、次のような単純な構造に値を与えるカーネルのポイントを調べるとき

struct pt{
int i;
int j;
int k;
}

複雑なことをしているわけではなく、メンバーに値を指定する必要があることは明らかですが、...

スタックの位置 0 を要求されましたが、スタックには 0 要素しかありません。

なので、配列ではないにしても、その時点でメモリのアラインメントに問題があるのではないかと考えています。そこで、ヘッダーファイルの定義を次のように変更します

struct __align__(16) pt{
int i;
int j;
int k;
}

しかし、コンパイラが同じ定義を使用するホスト コード ファイルをコンパイルしようとすると、次のエラーが発生します。

エラー: 数値定数エラーの前に unqualified-id が必要です: 数値定数エラーの前に ')' が必要です: ';' の前にコンストラクタ、デストラクタ、または型変換が必要です トークン

では、ホスト構造とデバイス構造に対して 2 つの異なる定義を使用する必要があるのでしょうか ???

さらに、アラインメントの論理を一般化する方法をお聞きしたいと思います。私はコンピューター科学者ではないので、プログラミング ガイドの 2 つの例は全体像を把握するのに役立ちません。

たとえば、次の 2 つをどのように配置すればよいでしょうか。または、6 つの float を持つ構造をどのように配置する必要がありますか? または4つの整数?繰り返しますが、私はそれらの配列を使用していませんが、カーネルまたは _ device _ 関数内でこれらの構造を使用して多くの変数を定義しています。

struct {
    int a;
    int b;
    int c;
    int d;
    float* el;    
} ;

 struct {
    int a;
    int b
    int c
    int d
    float* i;
    float* j;
    float* k;
} ;

アドバイスやヒントを事前にありがとう

4

2 に答える 2

30

この投稿には多くの質問があります。CUDA プログラミング ガイドは CUDA での配置について非常によく説明しているので、ガイドでは明らかでないいくつかのことについて説明します。

まず、ホスト コンパイラでエラーが発生する理由は、ホスト コンパイラが__align(n)__意味を認識していないためです。つまり、構文エラーが発生しています。必要なのは、プロジェクトのヘッダーに次のようなものを入れることです。

#if defined(__CUDACC__) // NVCC
   #define MY_ALIGN(n) __align__(n)
#elif defined(__GNUC__) // GCC
  #define MY_ALIGN(n) __attribute__((aligned(n)))
#elif defined(_MSC_VER) // MSVC
  #define MY_ALIGN(n) __declspec(align(n))
#else
  #error "Please provide a definition for MY_ALIGN macro for your host compiler!"
#endif

では、ホスト構造とデバイス構造に対して 2 つの異なる定義が必要でしょうか?

いいえ、MY_ALIGN(n)このように使用してください

struct MY_ALIGN(16) pt { int i, j, k; }

たとえば、次の 2 つをどのように配置すればよいでしょうか。

最初に(または任意のホスト コンパイラ フレーバー)、構造体のメモリがバイト__align(n)__の倍数であるメモリ内のアドレスで始まるように強制します。n構造体のサイズが の倍数でない場合、nそれらの構造体の配列に、各構造体が適切に配置されるようにパディングが挿入されます。の適切な値を選択するにはn、必要なパディングの量を最小限に抑える必要があります。プログラミング ガイドで説明されているように、ハードウェアでは、各スレッドが 1、2、4、8、または 16 バイトに整列されたワードを読み取る必要があります。そう...

struct MY_ALIGN(16) {
  int a;
  int b;
  int c;
  int d;
  float* el;    
};

この場合、16 バイトのアラインメントを選択するとします。32 ビット マシンでは、ポインターは 4 バイトを使用するため、構造体は 20 バイトを使用します。16 * (ceil(20/16) - 1) = 1216 バイトのアラインメントでは、構造体ごとにバイトが無駄になります。64 ビット マシンでは、ポインターが 8 バイトであるため、構造体ごとに 8 バイトしか無駄になりません。MY_ALIGN(8)代わりに使うことでゴミを減らすことができます。トレードオフは、メモリから構造体をロードするために、ハードウェアが 2 つの 16 バイト ロードの代わりに 3 つの 8 バイト ロードを使用する必要があることです。負荷によるボトルネックがない場合、これはおそらく価値のあるトレードオフです。この構造体を 4 バイト未満に整列させたくないことに注意してください。

struct MY_ALIGN(16) {
  int a;
  int b
  int c
  int d
  float* i;
  float* j;
  float* k;
};

この場合、16 バイト アラインメントを使用すると、32 ビット マシンでは構造体ごとに 4 バイトしか無駄にならず、64 ビット マシンでは 8 バイトしか無駄になりません。16 バイトのロードが 2 回 (64 ビット マシンでは 3 回) 必要になります。8 バイトにアラインする場合、4 バイト アラインメント (64 ビット マシンでは 8 バイト) で無駄を完全に排除できますが、過剰な負荷が発生します。繰り返しますが、トレードオフです。

または、6 つの float を持つ構造をどのように配置する必要がありますか?

繰り返しになりますが、トレードオフ: 構造体ごとに 8 バイトを浪費するか、構造体ごとに 2 回の読み込みが必要になります。

または4つの整数?

ここにトレードオフはありません。MY_ALIGN(16).

繰り返しますが、私はそれらの配列を使用していませんが、カーネルまたは _ device _ 関数内でこれらの構造を使用して多くの変数を定義しています。

うーん、これらの配列を使用していない場合は、整列する必要はまったくないかもしれません。しかし、どのように割り当てていますか? お気づきかもしれませんが、こうした無駄はすべて考慮することが重要です。これは、構造体の配列よりも配列の構造体を優先するもう 1 つの正当な理由です。

于 2012-10-08T10:21:56.270 に答える
8

最近では、 (現在の CUDA と互換性のあるバージョンを含む) でサポートされている C++11alignas指定子と IIANMも使用する必要があります。これにより、マクロに頼る必要がなくなります。g++nvcc

于 2016-03-21T18:50:45.193 に答える