Howto vblend 32位整数? 或者:为什么没有_mm256_blendv_epi32?

我正在使用AVX2 x86 256位SIMD扩展。 我想做一个32位整数组件if-then-else指令。 在英特尔文档中,这样的指令称为vblend。

Intel内部指南包含函数_mm256_blendv_epi8。 这个function几乎可以满足我的需求。 唯一的问题是它适用于8位整数。 遗憾的是,文档中没有_mm256_blendv_epi32。 我的第一个问题是:为什么这个function不存在? 我的第二个问题是:如何模仿它?

经过一番搜索后,我找到了_mm256_blendv_ps,这就是我想要的32位浮点数。 此外,我发现了转换函数_mm256_castsi256_ps和_mm256_castps_si256,它们从整数转换为32位浮点数并返回。 把这些放在一起给出:

inline __m256i _mm256_blendv_epi32 (__m256i a, __m256i b, __m256i mask){ return _mm256_castps_si256( _mm256_blendv_ps( _mm256_castsi256_ps(a), _mm256_castsi256_ps(b), _mm256_castsi256_ps(mask) ) ); } 

虽然这看起来像5个函数,但其​​中4个只是美化的强制转换,而另一个直接映射到处理器指令。 因此,整个function归结为一个处理器指令。

因此,真正令人尴尬的部分是似乎有一个32位的blendv,除了缺少相应的内在函数。

是否有一些边境案例,这将失败? 例如,当整数位模式恰好代表浮点NAN时会发生什么? blendv会忽略这个还是会引发一些信号?

万一这是有效的:我是否正确,有一个8位,一个32位和一个64位的blendv但是缺少一个16位的blendv?

我的代码依赖于blendv只检查最高位。

你有两个不错的选择:

  • 使用算术右移31来广播每个元素中的高位,以设置VPBLENDVB( _mm256_blendv_epi8 ) 。 即VPSRAD: mask=_mm256_srai_epi32(mask, 31)

    对于port0,英特尔Haswell的VPSRAD为1-uop。 (Skylake的吞吐量更高:p01)。 如果你的算法在端口0上遇到瓶颈(例如整数乘法和移位),那就不太好了。

  • 使用VBLENDVPS 。 你是正确的,所有的演员表只是为了让编译器满意,VBLENDVPS将在一条指令中完全按照你的意愿行事。

    但是,当将整数结果转发到FP混合单元时,Intel SnB系列CPU的旁路延迟延迟为1个周期,而将混合结果转发给其他整数指令时则具有另外1c延迟。

有关旁路延迟延迟的更多信息,请参阅Agner Fog的微操指南 。 这就是他们不为FP指令制作__m256i内在函数的原因,反之亦然。 请注意,由于Sandybridge,FP shuffle 没有额外的延迟来转发/转发到PADDD之类的指令。 因此,如果PUNPCK *或PALIGNR没有完全符合您的要求,SHUFPS是组合来自两个整数向量的数据的好方法。 (关于整数的SHUFPS甚至在Nehalem也是值得的,它在两个方面都有2c的惩罚)。

尝试两种方式和基准 。 无论哪种方式都可以更好,取决于周围的代码。

与uop吞吐量/指令计数相比,延迟可能无关紧要。 另请注意,如果您只是将结果存储到内存中,则存储指令不关心数据来自哪个域。

但是,如果您将此作为长依赖链的一部分使用,则可能值得额外的指令来避免混合数据的额外2个周期的延迟。

请注意,如果掩码生成位于关键路径上,那么VPSRAD的1周期延迟等于旁路延迟延迟,因此使用FP混合只是掩码 – >结果链的1个额外延迟周期,而2数据 – >结果链的额外周期。


例如,当整数位模式恰好代表浮点NAN时会发生什么?

BLENDVPS不关心。 英特尔的insn参考手册完整记录了指令可以/不能执行的所有操作 ,而SIMD浮点exception :无意味着这不是问题。 另请参阅x86标记wiki以获取文档链接。

FP blend / shuffle / bitwise-boolean / load / store指令不关心NaN。 只有执行实际FP数学运算的指令(包括CMPPS,MINPS和类似的东西)才会引发FPexception,或者可能会因非正规性而减速。


我是否正确,有一个8位,一个32位和一个64位的blendv但是缺少一个16位的blendv?

是。 但是有32位和16位算术移位,因此使用8位粒度混合最多需要一条额外的指令。 (没有PSRAQ,因此64位整数的混合通常最好使用BLENDVPD,除非掩模生成偏离关键路径和/或相同的掩码将在关键路径上重复使用多次。)

最常见的用例是比较掩码,其中每个元素都是全1或全0,因此您可以与PAND / PANDN => POR混合。 当然,巧妙的技巧只会使掩码的符号位与真值保留,可以节省指令和延迟,特别是因为变量混合比三个布尔按位指令快一些。 (例如,ORPS两个浮点向量,看看它们是否都是非负的,而不是2x CMPPS和ORing掩码。如果你不关心负零,这可以很好用)。