【发布时间】:2018-09-07 15:43:28
【问题描述】:
我试图弄清楚我可以在矢量硬件中装入多少东西。以支持 Intel AVX-512 的硬件为例,我可以将 8 个双精度(64 位)或 16 个单精度(32 位)放入我的向量中。但是,如果我在 64 位机器上运行,那么我的默认指针大小很可能是 64 位。因此,如果我想取消引用一个指针(或者只是使用数组语法访问和数组),那么这将需要一个 64 位整数操作。这似乎向我表明,在 64 位机器上,我可以拥有的最小分区将是 64 位数据类型。
然后考虑我在下面的 MWE,我希望编译器会看到我只处理 32 位对象(或更小)。鉴于我预计如果我可以将向量划分为 32 位数据类型而不是使用 64 位数据类型,那么减少/计算(假设我正在做一些计算量更大且带宽限制更少的事情)将在一半时间内完成。
- 有没有办法查询编译器使用的向量分区的粒度? (避免挖掘生成的程序集)。
- 在 64 位机器上,如果假定内存地址为 64 位,向量如何划分为小于 64 位的数据类型?
在我看来,如果我有向量寄存器并且我想做向量运算,那么如果我需要
n向量寄存器,其中每个寄存器都被分成m-bits 的数据类型,那么任何代码部分我希望矢量化不能使用大于m的数据类型。 (?)
MWE
使用 icc 18.0.0 和 -mkl -O2 -qopenmp -qopt-report 编译,其中优化报告验证 for 循环矢量化。
#include <stdlib.h>
#include <stdio.h>
#define N 1024
int main(int argc, char **argv)
{
unsigned int a[N];
for (unsigned int i = 0; i < N; i++) a[i] = i;
unsigned int z[N];
unsigned int *b = a;
printf("Sizes (Bytes)\n");
printf("Pointer = %d\n", sizeof(b));
printf("Unsigned int = %d\n", sizeof(*b));
printf("Array = %d\n\n", sizeof(a));
unsigned int sum = 0;
#pragma omp simd reduction(+:sum)
for (unsigned int i = 0; i < N; i++)
{
z[i] = 4 * a[i];
unsigned int squares = a[i] * a[i]; // Possibly some more complex sequence of operations.
sum += squares;
}
for (unsigned int i = 0; i < N; i += N/4) printf("z[%d] = %d\n", i, z[i]);
printf("\nsum = %d\n", sum);
}
我的机器上的输出是:
Sizes (Bytes)
Pointer = 8
Unsigned int = 4
Array = 4096
z[0] = 0
z[256] = 1024
z[512] = 2048
z[768] = 3072
sum = 357389824
【问题讨论】:
-
为什么每种类型都必须至少和指针一样大?
-
SIMD 向量中数据类型的宽度与指针大小无关,除非您正在执行涉及聚集或分散的操作。
-
在 x86 上,有很多 SIMD 指令对字节元素进行操作。这是最低限度;没有任何对半字节进行操作的。查看felixcloutier.com/x86/index.html 中的所有
p...b说明。就像pshufb:一个字节粒度的洗牌,它从寄存器中获取洗牌控制。您可以使用它从 16 元素表中并行执行 16 次查找,或者以任何您想要的方式重新排列向量中的字节。或者paddb:添加压缩(整数)字节,字节之间没有进位。
标签: c vectorization hardware intel simd