CUDA提供了像uint2
、uint4
等内置向量数据类型。使用这些数据类型有什么优点?
假设我有一个由两个值A和B组成的元组。一种将它们存储在内存中的方法是分配两个数组。第一个数组存储所有A值,第二个数组以对应于A值的索引存储所有B值。另一种方法是分配一个uint2
类型的数组。哪种方式应该使用?哪种方式被推荐?uint3
的成员,即x
、y
、z
是否在内存中相邻地存在?
CUDA提供了像uint2
、uint4
等内置向量数据类型。使用这些数据类型有什么优点?
假设我有一个由两个值A和B组成的元组。一种将它们存储在内存中的方法是分配两个数组。第一个数组存储所有A值,第二个数组以对应于A值的索引存储所有B值。另一种方法是分配一个uint2
类型的数组。哪种方式应该使用?哪种方式被推荐?uint3
的成员,即x
、y
、z
是否在内存中相邻地存在?
这可能有点推测性,但可能会补充@ArchaeaSoftware的答案。
我主要熟悉Compute Capability 2.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字节。我不确定这意味着什么。x
值,则由于128字节事务,其他值(y
、z
、w
)将被拉入L1。当warp稍后尝试访问它们时,它们可能不再在L1中,因此必须发出新的全局内存事务。此外,如果编译器能够发出更宽的指令以同时读取更多的值,以备将来使用,它将使用寄存器来存储这些值在加载点和使用点之间,从而可能增加内核的寄存器使用量。x
数组中读取时,只会加载x
值在128字节事务中。这可能导致较少的事务、对缓存的较少依赖以及计算和内存操作之间更均匀的分布。float4
结构中,__builtin_align__(16)
的确切作用是什么? - username_4567__builtin_align__
是__align__
的包装器。您可以通过查看CUDA头文件来确认。__align__
应该在CUDA C编程指南中有描述。 - harrism至于是否应该使用uint2数组(结构体数组或AoS)还是两个uint数组(数组的结构体或SoA),没有简单的答案——这取决于应用程序。对于方便大小的内置类型(2x32位或4x32位),AoS具有优势,因为您只需要一个指针来加载/存储每个数据元素。SoA需要多个基指针,或者至少需要多个偏移量和单独的加载/存储操作才能处理每个元素;但对于有时仅操作子集元素的工作负载可能更快。
作为使用AoS的好例子,请查看nbody示例(它使用float4来保存每个粒子的XYZ+质量)。Black-Scholes示例使用SoA,可能是因为float3是一种不方便的元素大小。
uint2
和uint4
这样的结构(如果它们适用于您的数据和算法)是有优势的,因为它们可以增加每个线程的事务大小,从而更有效地利用可用带宽。您可以创建自己的自定义结构,但要确保它们像CUDA提供的结构一样指定对齐方式。 - harrism