16

uint2CUDA は、などの組み込みのベクトル データ型を提供uint4します。これらのデータ型を使用する利点はありますか?

A と B の 2 つの値で構成されるタプルがあるとします。それらをメモリに格納する 1 つの方法は、2 つの配列を割り当てることです。最初の配列はすべての A 値を格納し、2 番目の配列はすべての B 値を A 値に対応するインデックスに格納します。もう 1 つの方法は、タイプ の配列を 1 つ割り当てることuint2です。どちらを使用する必要がありますか? おすすめの方法は?uint3ie xy、のメンバーはzメモリ内に並んで存在しますか?

4

3 に答える 3

6

これは少し推測になりますが、@ArchaeaSoftwareの答えに追加される可能性があります。

私は主にComputeCapability2.0(Fermi)に精通しています。このアーキテクチャでは、8ビットタイプと16ビットタイプを除いて、ベクトル化されたタイプを使用することにパフォーマンス上の利点はないと思います。

char4の宣言を見てください:

struct __device_builtin__ __align__(4) char4
{
    signed char x, y, z, w;
};

タイプは4バイトに揃えられます。何をするのかわかりません__device_builtin__。多分それはコンパイラでいくつかの魔法を引き起こします...

float1、、、および:float2の宣言については少し奇妙に見えます。float3float4

struct __device_builtin__ float1
{
    float x;
};

__cuda_builtin_vector_align8(float2, float x; float y;);

struct __device_builtin__ float3
{
    float x, y, z;
};

struct __device_builtin__ __builtin_align__(16) float4
{
    float x, y, z, w;
};

float2何らかの形で特別な扱いを受けます。float3アラインメントのない構造体であり、float416バイトにアラインメントされます。どうしたらいいのかわからない。

グローバルメモリトランザクションは128バイトで、128バイトにアラインされます。トランザクションは常に一度にフルワープに対して実行されます。ワープがメモリトランザクションを実行する関数、たとえばグローバルメモリからの32ビットロードに到達すると、チップはその時点で、ワープ内の32スレッドすべてにサービスを提供するために必要な数のトランザクションを実行します。したがって、アクセスされたすべての32ビット値が単一の128バイト行内にある場合、必要なトランザクションは1つだけです。値が異なる128バイトの行からのものである場合、複数の128バイトのトランザクションが実行されます。トランザクションごとに、データがメモリからフェッチされている間、ワープは約600サイクル保留されます(L1またはL2キャッシュにある場合を除く)。

したがって、どのタイプのアプローチが最高のパフォーマンスを提供するかを見つけるための鍵は、どのアプローチが128バイトのメモリトランザクションを最も少なくするかを検討することだと思います。

組み込みのベクトル型が単なる構造体であり、その一部には特別な配置があると仮定すると、ベクトル型を使用すると、値がインターリーブ方式でメモリ(構造体の配列)に格納されます。したがって、ワープがxその時点ですべての値をロードしている場合、128バイトのトランザクションのために、他の値(、、y)がL1にプルインされます。後でワープがそれらにアクセスしようとすると、それらがL1に存在しなくなった可能性があるため、新しいグローバルメモリトランザクションを発行する必要があります。また、コンパイラがより広い命令を発行して同時により多くの値を読み取ることができる場合、将来の使用のために、ロードポイントと使用ポイントの間の値を格納するためにレジスタを使用し、おそらくレジスタの使用量を増やしますカーネルの。zw

一方、値が配列の構造体にパックされている場合は、可能な限り少ないトランザクションで負荷を処理できます。したがって、x配列から読み取る場合x、128バイトのトランザクションには値のみがロードされます。これにより、トランザクションが少なくなり、キャッシュへの依存度が低くなり、計算操作とメモリ操作の間でより均等に分散される可能性があります。

于 2012-09-09T22:27:19.110 に答える
4

CUDA の組み込みタプル ([u]int[2|4]、float[2|4]、double[2]) に本質的な利点があるとは思いません。それらは主に利便性のために存在します。同じレイアウトで独自の C++ クラスを定義することができ、コンパイラはそれらに対して効率的に動作します。ハードウェアにはネイティブの 64 ビットと 128 ビットのロードがあるため、生成されたマイクロコードを確認して確認する必要があります。

uint2 の配列 (構造体の配列または AoS) を使用するか、uint の 2 つの配列 (配列の構造体または SoA) を使用するかについては、簡単な答えはありません。アプリケーションによって異なります。便利なサイズ (2x32 ビットまたは 4x32 ビット) の組み込み型の場合、AoS には、各データ要素をロード/ストアするために必要なポインターが 1 つだけであるという利点があります。SoA には、複数のベース ポインター、または少なくとも複数のオフセットと要素ごとの個別のロード/ストア操作が必要です。ただし、要素のサブセットでのみ動作することがあるワークロードでは、より高速になる場合があります。

AoS を効果的に使用するワークロードの例として、nbody サンプル (各パーティクルの XYZ+質量を保持するために float4 を使用) を見てください。Black-Scholes のサンプルは SoA を使用していますが、これはおそらく float3 が不便な要素サイズであるためです。

于 2012-09-09T21:44:50.757 に答える