これは少し推測になりますが、@ArchaeaSoftwareの答えに追加される可能性があります。
私は主にComputeCapability2.0(Fermi)に精通しています。このアーキテクチャでは、8ビットタイプと16ビットタイプを除いて、ベクトル化されたタイプを使用することにパフォーマンス上の利点はないと思います。
char4の宣言を見てください:
struct __device_builtin__ __align__(4) char4
{
signed char x, y, z, w;
};
タイプは4バイトに揃えられます。何をするのかわかりません__device_builtin__
。多分それはコンパイラでいくつかの魔法を引き起こします...
float1
、、、および:float2
の宣言については少し奇妙に見えます。float3
float4
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
アラインメントのない構造体であり、float4
16バイトにアラインメントされます。どうしたらいいのかわからない。
グローバルメモリトランザクションは128バイトで、128バイトにアラインされます。トランザクションは常に一度にフルワープに対して実行されます。ワープがメモリトランザクションを実行する関数、たとえばグローバルメモリからの32ビットロードに到達すると、チップはその時点で、ワープ内の32スレッドすべてにサービスを提供するために必要な数のトランザクションを実行します。したがって、アクセスされたすべての32ビット値が単一の128バイト行内にある場合、必要なトランザクションは1つだけです。値が異なる128バイトの行からのものである場合、複数の128バイトのトランザクションが実行されます。トランザクションごとに、データがメモリからフェッチされている間、ワープは約600サイクル保留されます(L1またはL2キャッシュにある場合を除く)。
したがって、どのタイプのアプローチが最高のパフォーマンスを提供するかを見つけるための鍵は、どのアプローチが128バイトのメモリトランザクションを最も少なくするかを検討することだと思います。
組み込みのベクトル型が単なる構造体であり、その一部には特別な配置があると仮定すると、ベクトル型を使用すると、値がインターリーブ方式でメモリ(構造体の配列)に格納されます。したがって、ワープがx
その時点ですべての値をロードしている場合、128バイトのトランザクションのために、他の値(、、y
)がL1にプルインされます。後でワープがそれらにアクセスしようとすると、それらがL1に存在しなくなった可能性があるため、新しいグローバルメモリトランザクションを発行する必要があります。また、コンパイラがより広い命令を発行して同時により多くの値を読み取ることができる場合、将来の使用のために、ロードポイントと使用ポイントの間の値を格納するためにレジスタを使用し、おそらくレジスタの使用量を増やしますカーネルの。z
w
一方、値が配列の構造体にパックされている場合は、可能な限り少ないトランザクションで負荷を処理できます。したがって、x
配列から読み取る場合x
、128バイトのトランザクションには値のみがロードされます。これにより、トランザクションが少なくなり、キャッシュへの依存度が低くなり、計算操作とメモリ操作の間でより均等に分散される可能性があります。