【问题标题】:Minimum SIMD vector width data type最小 SIMD 向量宽度数据类型
【发布时间】: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


【解决方案1】:

有没有办法查询向量分区的粒度 编译器已经使用了? (避免挖掘结果 组装)。

我认为您问题的最佳答案是:“通过 opt-report 或 Vectorization Advisor 等工具了解您的矢量长度是多少”。

VL 有几种类型和几种可能的定义:

  • 向量化时,Compiler 会选择最优的“向量长度”(VL)。我们称之为“编译器定义的循环 VL”。 循环的 VL 可以定义为“打包”结果向量运算的大量标量迭代。因此,在简单的非 FMA DP AVX-512 情况下,VL 通常等于 4。
  • VL 也可以不是为循环定义,而是为指令(或 以某种方式用于指令数据操作数),但是“最佳”或“当前” per-instruction VL 可能与结果循环的 VL 显着不同。

  • 您可能还会想到另一个指标,即硬件定义的循环 (或指令)VL。

考虑到目标 ISA、精度等,可以放入单个向量寄存器的数据元素的数量几乎等于硬件定义的每条指令 VL。 编译器定义的每循环 VL 可能经常高于或低于“支配”每指令硬件定义的 VL。对于混合了不同数据类型的循环或使用寄存器拆分(不溢出)或多泵等技术针对微架构额外优化的循环尤其如此。

要了解循环的 VL,请在使用英特尔编译器重新编译代码时使用 -qopt-report 标志。

要了解您的循环和指令的编译器定义和硬件定义的 VL(不仅适用于英特尔编译器),以及时间指标、步幅、二进制 ISA 静态分析(如果您最终需要它,甚至可以进行汇编), FLOPS 和 AVX 指令混合数据 - 使用 Intel Advisor(调查分析):

【讨论】:

  • 您是在谈论 AVX-512 但仅使用 YMM 寄存器来避免降低最大涡轮增压吗?一些编译器不会自动使用 ZMM 寄存器进行自动矢量化,即使 ZMM 可用,它也更喜欢 YMM,因为它可以减慢程序的 rest 速度,以便在一个循环中使用 ZMM 指令,结果证明不是整体上很重要。无论如何,8 个doubles 适合 ZMM 512 位向量,或 4 个doubles 适合 YMM 256 位向量。
  • 你说得对,在 SKX 编译器上可能更喜欢 YMM 代码生成。然而,这不是我想到的(屏幕截图是在 KNL 上拍摄的)。我说的是,考虑到例如AVX(1) 双精度代码,您可以轻松获得 Compiler_VL =2、Compiler_VL =8 甚至 Compiler_VL = 16。一个示例是包含单精度和双精度组合的循环。另一个例子是 SNB 的寄存器拆分。第三个例子是多抽(一种微展开)。当然,用户也可以通过 OMP4.x SIMD 明确请求非默认 VL。
【解决方案2】:

这似乎向我表明,在 64 位机器上,我可以拥有的最小分区是 64 位数据类型。

这个假设是错误的。

用一个(尴尬的)类比来说明,邮政地址的长度(以符号表示)与房子的大小无关。指针的宽度与其引用的数据大小无关。

对于在给定类型的硬件上可以处理多小的数据有一个下限。它被称为 byte(在现代机器上是 8 位,又名 octet,但在古代机器上也可以是 10 或 6 位)。然而,通常没有更高的界限。例如,在 Intel 64 中,XSAVE 系列指令引用了一个近 4 KB 长的内存块,具有相同的 32/64 位指针。

以支持英特尔 AVX-512 的硬件为例,我可以将 8 个双精度(64 位)或 16 个单精度(32 位)放入我的向量中。

或者您可以容纳 32 个半浮点数(16 位)或 64 个字节。不确定是否有 AVX-512 指令在半字节(4 位块)上运行。

有没有办法查询编译器使用的向量分区的粒度? (避免挖掘生成的程序集)。

同样,编译器选择的下限由程序中所选数据类型的宽度决定。如果使用int,则粒度至少为sizeof(int)字节,如果longsizeof(long) 等。不太可能使用超出必要的类型,因为它会导致机器指令的语义差异这应该被考虑在内。例如,如果编译器出于未知原因选择使用划分为 uint64_t 块的 SIMD 向量来对 uint32_t 块的向量进行操作,则它必须隐藏溢出行为的差异,这会导致性能下降罚款。

我不知道是否有 OMP pragma 可以查询此类信息。这不太可能,因为同一个二进制文件可能在运行时动态选择多个代码路径(程序启动,至少英特尔编译器使用所谓的调度),所以编译时查询是不可能的,我看不到太多在运行时查询中使用。

在 64 位机器上,如果假设 64 位内存地址,向量如何划分为小于 64 位的数据类型?

有简单的机器指令以不同的方式解释相同的 SIMD 寄存器。在 Intel 64 中,有各种各样的示例(示例取自最近的 Intel 软件开发手册):

  • VDBPSADBW - 无符号字节(8 位)
  • 上的双块压缩和绝对差 (SAD)
  • VCVTPH2PS — 将 16 位 FP 值转换为单精度 FP 值
  • VCVTPS2UDQ - 将压缩单精度(32 位)浮点值转换为压缩无符号双字整数值
  • VCVTQQ2PD - 将压缩 四字(64 位)整数转换为压缩双精度浮点值

【讨论】:

  • 即使在 AVX512 中也没有半字节元素指令。 x86 有DAA to adjust the result of a packed-BCD add,但它使用一个位标志(AF),所以它甚至不是真正的SWAR。它适用于 AL 中的 2 个 BCD 数字,不适用于 AX 中的 4。 AVX512 有很多疯狂的东西,比如 VPMULTISHIFTQB,但那是 qword 元素中的未对齐移位。
  • 为什么选择仅 AVX512BW 的 VDBPSADBW 而不是更简单的 vpsadbw,后者在每个 64 位元素内执行无符号字节差异的 SAD。或者像paddb这样简单的东西:添加字节元素:P
  • @PeterCordes 确实,一个奇怪的例子选择。然而,我很困,在写完答案时,我需要处理其他事情;现在我无法解释那个列表(-%
猜你喜欢
  • 2020-02-15
  • 2014-10-31
  • 2011-10-18
  • 2015-03-26
  • 2011-11-06
  • 2014-12-23
  • 2013-05-14
  • 2021-04-09
  • 2012-11-13
相关资源
最近更新 更多