我正在使用 AVX2 x86 256 位 SIMD 扩展。我想做一个 32 位整数组件明智的 if-then-else 指令。在 Intel 文档中,这样的指令称为 vblend。
Intel 内在指南包含函数 _mm256_blendv_epi8。这个功能几乎可以满足我的需求。唯一的问题是它适用于 8 位整数。不幸的是,文档中没有 _mm256_blendv_epi32。我的第一个问题是:为什么这个功能不存在?我的第二个问题是:如何模仿它?
经过一番搜索,我发现 _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 个只是美化的强制转换,一个直接映射到处理器指令上。因此,整个函数归结为一条处理器指令。
因此,真正尴尬的部分是似乎有一个 32 位的 blendv,只是缺少相应的内在函数。
是否有一些边境案例会惨遭失败?例如,当整数位模式恰好代表一个浮点 NAN 时会发生什么? Blendv 是简单地忽略这一点还是会发出一些信号?
如果这有效:我是否正确地认为有一个 8 位、一个 32 位和一个 64 位的 blendv 但缺少 16 位的 blendv?
最佳答案
如果您的 mask
已经是整个 32 位元素的全零/全一 (如 vpcmpgtd
结果),请直接使用 _mm256_blendv_epi8
。
My code relies on blendv only checking the highest bit.
那么你有两个不错的选择:
_mm256_blendv_epi8
) 。即 VPSRAD: mask=_mm256_srai_epi32(mask, 31)
。VPSRAD 在 Intel Haswell 上是 1-uop,用于端口 0。 (Skylake 上的更多吞吐量:p01)。如果您的算法在端口 0 上出现瓶颈(例如整数乘法和移位),这不是很好。
static inline
__m256i blendvps_si256(__m256i a, __m256i b, __m256i mask) {
__m256 res = _mm256_blendv_ps(_mm256_castsi256_ps(a), _mm256_castsi256_ps(b), _mm256_castsi256_ps(mask));
return _mm256_castps_si256(res);
}
但是, Intel SnB 系列 CPU 在将整数结果转发到 FP 混合单元时具有 1 个周期的旁路延迟延迟 ,在将混合结果转发到其他整数指令时还有 1c 延迟。如果延迟不是瓶颈,这可能不会影响吞吐量。
有关旁路延迟延迟的更多信息,请参阅 Agner Fog's microach guide 。这就是他们不为 FP 指令制作
__m256i
内在函数的原因,反之亦然。请注意,自 Sandybridge 以来,FP shuffle 没有额外的延迟来转发/转发 PADDD 等指令。因此,如果 PUNPCK* 或 PALIGNR 不完全符合您的要求,那么 SHUFPS 是组合来自两个整数 vector 的数据的好方法。 (整数上的 SHUFPS 即使在 Nehalem 上也是值得的,如果吞吐量是你的瓶颈,它在两种情况下都有 2c 的惩罚)。尝试两种方式并对 进行基准测试。无论哪种方式都可能更好,具体取决于周围的代码。
与 uop 吞吐量/指令数相比,延迟可能无关紧要。另请注意,如果您只是将结果存储到内存中,则存储指令不关心数据来自哪个域。
但是,如果您将其用作长依赖链的一部分,那么可能值得使用额外的指令来避免混合数据的额外 2 个延迟周期。
请注意,如果掩码生成在关键路径上,则 VPSRAD 的 1 个周期延迟等效于旁路延迟延迟,因此使用 FP 混合对于掩码->结果链仅增加 1 个周期的延迟,而 2数据->结果链的额外周期。
For example, what happens when the integer bit pattern happens to represent a floating point NAN?
BLENDVPS 不在乎。 Intel 的 insn ref manual fully documents everything an instruction can/can't do 和 SIMD 浮点异常 : None 表示这不是问题。另请参阅 x86 标记 wiki 以获取文档链接。
FP blend/shuffle/bitwise-boolean/load/store 指令不关心 NaN。只有进行实际 FP 数学运算的指令(包括 CMPPS、MINPS 和类似的东西)才会引发 FP 异常或者可能会因非规范而减慢速度。
Am I correct that there is a 8-bit, a 32-bit and a 64-bit blendv but a 16-bit blendv is missing?
是的。但是有 32 位和 16 位算术移位,因此使用 8 位粒度混合最多需要一条额外的指令。 (没有 PSRAQ,因此 64 位整数的 blendv 通常最好使用 BLENDVPD 完成,除非掩码生成不在关键路径和/或相同的掩码将在关键路径上重复使用多次。)
最常见的用例是比较掩码,其中每个元素都已经是全 1 或全零,因此您可以与 PAND/PANDN => POR 混合。当然,只保留掩码的符号位和真值的巧妙技巧可以节省指令和延迟,特别是因为可变混合比三个 bool 位指令要快一些。 (例如 ORPS 两个浮点 vector 以查看它们是否都是非负数,而不是 2x CMPPS 和 ORing 掩码。如果您不关心负零,或者您乐于将下溢处理为
-0.0
,这可以很好地工作为阴性)。
关于c++ - 如何混合 32 位整数?或 : Why is there no _mm256_blendv_epi32?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40746656/