使用AVX2收集指令时加载地址计算

时间:2013-04-24 13:34:42

标签: x86 sse simd avx2

查看AVX2内在函数文档,收集了加载指令,例如VPGATHERDD

__m128i _mm_i32gather_epi32 (int const * base, __m128i index, const int scale);

从文档中我不清楚计算的加载地址是元素地址还是字节地址,即加载地址是否为元素i

load_addr = base + index[i] * scale;               // (1) element addressing ?

或:

load_addr = (char *)base + index[i] * scale;       // (2) byte addressing ?

Intel docs它看起来可能是(2),但是这并没有多大意义,因为收集的负载的最小元素大小是32位 - 为什么要加载未对齐的地址(即使用比例< 4)?

3 个答案:

答案 0 :(得分:9)

收集说明没有任何对齐要求。因此,如果不允许字节寻址则限制太多。

其他原因是一致性。通过SIB寻址,我们显然有 byte 地址:

MOV eax, [rcx + rdx * 2]

由于VPGATHERDD只是此MOV指令的向量化变体,因此我们不应期望与VSIB寻址有任何不同:

VPGATHERDD ymm0, [rcx + ymm2 * 2], ymm3

对于字节寻址的实际使用,我们可以有一个24位彩色图像,其中每个像素是3字节对齐的。我们可以使用单个VPGATHERDD指令加载8个像素,但前提是VSIB中的“scale”字段为“1”且VPGATHERDD使用 byte 寻址。

答案 1 :(得分:5)

根据英特尔AVX programming reference document available here中的描述判断,收集指令看起来像字节寻址。具体而言,请参阅VPGATHERDD指令说明(第389页)中的以下引用:

DISP: optional 1, 2, 4 byte displacement;
DATA_ADDR = BASE_ADDR + (SignExtend(VINDEX[i+31:i])*SCALE + DISP;

由于你可以使用1/2/4字节位移,我会假设整个内存地址是一个字节地址。虽然它可能不是常见的应用程序,但有时您可能希望从未对齐的地址读取32位或64位值。与ARM这样的东西相比,这是x86架构更灵活的事情之一;如果需要,您可以灵活地执行未对齐的访问,而不是像其他人那样触发CPU异常。

答案 2 :(得分:1)

  

为什么要从未对齐的地址加载(即使用比例< 4)?

未对齐的载荷不是缩放的唯一用例<元素大小。您可能只有索引是预先缩放的字节偏移量。或者考虑在指向结构的指针数组上对循环进行向量化:您可以使用零的基本“地址”或结构中的小整数偏移进行收集。

支持这个用例是英特尔设计asm指令来支持这一用例的一个原因,因为收集应该有助于编译器自动矢量化更多代码。它也很适合VSIB字节在机器码编码中非常接近SIB,但它们可以很容易地预先偏置比例因子,让你可以选择scale = 4,8,16,32(或8) ,qword道集16,32,64),带有2比特尺度场。

大于元素大小的比例因子在许多情况下也不明显有用,并且在收集之前使用单个左移指令很容易模拟。但无法解决烘焙比例因子,因此允许未缩放的索引显然是更灵活的设计选择。

其他用例:收集16位元素。收集后使用32位聚集并屏蔽每个元素的上半部分。 (或者只是把它拿着垃圾)。如果您的任何索引都是奇数(比例因子为2),那么会导致错位负载,因此如果它们跨越4k边界(与真正的16位聚集不同),它可能会很慢。

您还可以想象使用聚集作为解压缩功能的一部分,在一些解码之后,您有一个偏移向量到缓冲区,并且您需要任意4字节或8字节数据窗口。