缺少面具的AVX-512内在函数?

时间:2017-07-18 13:19:23

标签: c gcc intrinsics icc avx512

英特尔针对AVX-512 K *掩码指令的内在指南lists a number of intrinsics,但似乎有一些缺失:

  • KSHIFT {L / R}
  • KADD
  • KTEST

英特尔开发人员手册声称内在函数不是必需的,因为它们是由编译器自动生成的。一个人怎么做呢?如果这意味着__mmask *类型可以被视为常规整数,那么它会很有意义,但是像mask << 4这样的测试似乎会导致编译器将掩码移动到常规寄存器,移位它,然后移动回到面具。这是使用Godbolt最新的GCC和ICC -O2 -mavx512bw进行测试的。

同样值得注意的是,内在函数只处理__mmask16而不处理其他类型。我没有测试太多,但看起来ICC并不介意采用不正确的类型,但GCC似乎确实尝试确保掩码中只有16位,如果你使用内在函数。

我是不是在寻找上述指令的正确内在函数,以及其他__mmask *类型的变体,还是有其他方法可以实现相同的东西,而不需要求助于内联汇编?

1 个答案:

答案 0 :(得分:8)

英特尔的文档说,&#34;没有必要,因为它们是由编译器自动生成的&#34;事实上是正确的。然而,它并不令人满意。

但要理解为什么会这样,你需要看一下AVX512的历史。虽然这些信息都不是官方信息,但它基于证据强烈暗示。

面具内在因素陷入困境的原因可能是因为AVX512已经推出了#34;在没有充分前瞻性规划的情况下进入下一阶段。

第1阶段: Knights Landing

Knights Landing添加了512位寄存器,这些寄存器只有32位和64位数据粒度。因此,掩码寄存器永远不需要超过16位。

当英特尔设计这些第一套AVX512内在函数时,他们继续为几乎所有内容添加了内在函数 - 包括掩码寄存器。这就是为什么存在的掩码内在函数只有16位。它们只涵盖Knights Landing中存在的指令。 (虽然我无法解释为什么KSHIFT缺失)

在Knights Landing上,面具操作很快(2个周期)。但是在掩码寄存器和通用寄存器之间移动数据非常慢(5个周期)。因此,在完成掩码操作的地方很重要,并且让用户能够更精细地控制在掩码寄存器和GPR之间来回移动内容。

阶段2: Skylake Purley

Skylake Purley将AVX512扩展到覆盖字节粒状通道。并且这将掩码寄存器的宽度增加到完整的64位。第二轮还添加了KADDKTEST,这些在骑士登陆中并不存在。

这些新的掩码指令(KADDKTEST和现有的64位扩展名)是缺少其内在对应物的指令。

虽然我们并不确切地知道他们失踪的原因,但有一些强有力的证据支持它:

<强>编译器/语法:

在Knights Landing上,相同的掩模内在函数用于8位和16位掩码。没有办法区分它们。通过将它们扩展到32位和64位,它使得混乱变得更糟。换句话说,英特尔没有正确设计掩模内在函数。他们决定完全放弃它们而不是解决它们。

效果不一致

Skylake Purley的比特交叉掩码指令很慢。虽然所有逐位指令都是单周期的,但KADDKSHIFTKUNPACK等等都是4个周期。但是在掩模和GPR之间移动只有2个周期。

因此,将它们移动到GPR中并将它们移回原来通常会更快。但程序员不太可能知道这一点。因此,英特尔选择让编译器做出这个决定,而不是让用户完全控制掩码寄存器。

通过让编译器做出这个决定,这意味着编译器需要具有这样的逻辑。英特尔编译器目前正在执行,因为它将在某些(罕见)情况下生成kadd和系列。但海湾合作委员会没有。在GCC上,除了最琐碎的掩码操作之外的所有操作都将被移动到GPR并在那里完成。

最后的想法:

在Skylake Purley发布之前,我个人编写了很多AVX512代码,其中包含很多AVX512掩码。这些都是根据Skylake Purley的某些性能假设(单周期延迟)编写的。

根据我自己在Skylake X上的测试,我的一些依赖于位交叉操作的掩码内部代码比编译器生成的版本更慢,后者将它们移动到GPR并返回。当然,KADDKSHIFT是4个周期而不是1个。

当然,我更喜欢英特尔确实提供内在函数来为我们提供我想要的控制权。但如果您不知道自己在做什么,那么在这里(就性能而言)很容易出错。

<强>更新

目前还不清楚这种情况何时发生,但最新版本的英特尔内在指南有一套新的掩码内在函数,其新的命名约定涵盖了所有指令和宽度。这些新的内在函数取代了旧的内在函数。

所以这解决了整个问题。虽然编译器支持的程度仍然不确定。

示例:

  • _kadd_mask64()
  • _kshiftri_mask32()
  • _cvtmask16_u32()取代_mm512_mask2int()