这可能有点推测性,但可能会增加@ArchaeaSoftware 的答案。
我主要熟悉计算能力2.0(Fermi)。对于这种架构,我认为使用向量化类型没有任何性能优势,除了 8 位和 16 位类型。
查看 char4 的声明:
struct __device_builtin__ __align__(4) char4
{
signed char x, y, z, w;
};
该类型按 4 字节对齐。我不知道什么__device_builtin__
做。也许它会触发编译器中的一些魔法......
声明的事情看起来有点奇怪float1
, float2
, float3
and 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 字节。事务总是一次执行完整的扭曲。当 warp 到达执行内存事务的函数时,例如从全局内存加载 32 位,芯片将执行为 warp 中的所有 32 个线程提供服务所需的尽可能多的事务。因此,如果所有访问的 32 位值都在单个 128 字节行内,则只需要一次事务。如果这些值来自不同的 128 字节线,则执行多个 128 字节事务。对于每个事务,当从内存中获取数据时,warp 会被搁置大约 600 个周期(除非它位于 L1 或 L2 缓存中)。
因此,我认为找出哪种类型的方法可提供最佳性能的关键是考虑哪种方法导致的 128 字节内存事务最少。
假设内置向量类型只是结构体,其中一些具有特殊的对齐方式,使用向量类型会导致值以交错方式存储在内存中(结构体数组)。所以,如果扭曲正在加载所有x
该点的值,其他值(y
, z
, w
)将由于 128 字节事务而被拉入 L1。当 warp 稍后尝试访问这些数据时,它们可能不再位于 L1 中,因此必须发出新的全局内存事务。此外,如果编译器能够发出更宽的指令来同时读取更多值,以供将来使用,它将使用寄存器来存储加载点和使用点之间的值,也许会增加寄存器的使用内核的。
另一方面,如果将值打包到数组结构中,则可以使用尽可能少的事务来服务负载。所以,当从x
数组,仅x
值加载到 128 字节事务中。这可能会导致事务减少、对缓存的依赖减少以及计算和内存操作之间的分布更加均匀。