如何混合 32 位整数?或者:为什么没有_mm256_blendv_epi32?
Howto vblend for 32-bit integer? or: Why is there no _mm256_blendv_epi32?
我正在使用 AVX2 x86 256 位 SIMD 扩展。我想做一个 32 位整数组件明智的 if-then-else 指令。在 Intel 文档中,这样的指令称为 vblend。
英特尔内在指南包含函数 _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.
那么你有两个不错的选择:
广播每个元素中的高位使用算术右移 31 来设置 VPBLENDVB (_mm256_blendv_epi8
). i.e. VPSRAD: mask=_mm256_srai_epi32(mask, 31)
。
VPSRAD 在 Intel Haswell 上是 1-uop,用于端口 0。 (Skylake 上的吞吐量更高:p01)。如果您的算法在端口 0 上出现瓶颈(例如整数乘法和移位),这不是很好。
使用 VBLENDVPS 获得延迟吞吐量。你是对的,所有的强制转换只是为了让编译器满意,VBLENDVPS 将在一条指令中完全按照你的意愿去做。
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);
}
但是,英特尔 SnB 系列 CPU 在将整数结果转发到 FP 混合单元时具有旁路延迟延迟 1 个周期,在转发混合结果时还有 1c 延迟到其他整数指令。如果这不是长依赖链(跨迭代)的一部分,那么保存 uops 可能会更好,让 OoO exec 隐藏额外的延迟。
有关旁路延迟延迟的更多信息,请参阅 Agner Fog's microach guide。这就是他们不为 FP 指令制作 __m256i
内在函数的原因,反之亦然。请注意,自 Sandybridge 以来,FP 洗牌 不会 有额外的延迟来转发 from/to 指令,如 PADDD。因此,如果 PUNPCK* 或 PALIGNR 不能完全满足您的要求,SHUFPS 是一种合并来自两个整数向量的数据的好方法。 (即使在 Nehalem 上,整数上的 SHUFPS 也是值得的,如果吞吐量是您的瓶颈,它在双向都会有 2c 的惩罚)。
两种方式都试一下,基准测试。根据周围的代码,任何一种方式都可能更好。
与 uop 吞吐量/指令数相比,延迟可能无关紧要。另请注意,如果您只是将结果存储到内存中,存储指令并不关心数据来自哪个域。
但是,如果您将其用作长依赖链的一部分,那么如果关键路径通过被混合的数据,那么额外的指令可能是值得的,以避免混合数据的额外 2 个延迟周期混合,不是面膜。
请注意,如果掩码生成在关键路径上,则 VPSRAD 的 1 个周期延迟相当于旁路延迟延迟,因此使用 FP 混合只是掩码-> 结果的额外 1 个延迟周期链,与数据->结果链的 2 个额外周期相比。而且,如果您使用可以从 FP 或整数混合有效转发的指令来使用混合结果,那么使用 FP 混合是绝对的胜利,为相同的延迟节省指令(及其 uop)。
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, and SIMD Floating-Point Exceptions: None means that this isn't a problem. See also the x86 tag 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 完成,除非掩码生成可能偏离关键路径 and/or 相同的掩码将在关键路径上多次重复使用。)
最常见的用例是比较掩码,其中每个元素都已经是全一或全零,因此您可以与 PAND/PANDN => POR 混合。当然,只保留掩码的符号位和真值的巧妙技巧可以节省指令和延迟,特别是因为变量混合比三个布尔按位指令快一些。 (例如,ORPS 两个浮点向量以查看它们是否都是非负的,而不是 2x CMPPS 和对掩码进行 ORing。如果您不关心负零,或者您乐于将下溢处理为 -0.0
为负)。
我正在使用 AVX2 x86 256 位 SIMD 扩展。我想做一个 32 位整数组件明智的 if-then-else 指令。在 Intel 文档中,这样的指令称为 vblend。
英特尔内在指南包含函数 _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.
那么你有两个不错的选择:
广播每个元素中的高位使用算术右移 31 来设置 VPBLENDVB (
_mm256_blendv_epi8
). i.e. VPSRAD:mask=_mm256_srai_epi32(mask, 31)
。VPSRAD 在 Intel Haswell 上是 1-uop,用于端口 0。 (Skylake 上的吞吐量更高:p01)。如果您的算法在端口 0 上出现瓶颈(例如整数乘法和移位),这不是很好。
使用 VBLENDVPS 获得延迟吞吐量。你是对的,所有的强制转换只是为了让编译器满意,VBLENDVPS 将在一条指令中完全按照你的意愿去做。
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); }
但是,英特尔 SnB 系列 CPU 在将整数结果转发到 FP 混合单元时具有旁路延迟延迟 1 个周期,在转发混合结果时还有 1c 延迟到其他整数指令。如果这不是长依赖链(跨迭代)的一部分,那么保存 uops 可能会更好,让 OoO exec 隐藏额外的延迟。
有关旁路延迟延迟的更多信息,请参阅 Agner Fog's microach guide。这就是他们不为 FP 指令制作 __m256i
内在函数的原因,反之亦然。请注意,自 Sandybridge 以来,FP 洗牌 不会 有额外的延迟来转发 from/to 指令,如 PADDD。因此,如果 PUNPCK* 或 PALIGNR 不能完全满足您的要求,SHUFPS 是一种合并来自两个整数向量的数据的好方法。 (即使在 Nehalem 上,整数上的 SHUFPS 也是值得的,如果吞吐量是您的瓶颈,它在双向都会有 2c 的惩罚)。
两种方式都试一下,基准测试。根据周围的代码,任何一种方式都可能更好。
与 uop 吞吐量/指令数相比,延迟可能无关紧要。另请注意,如果您只是将结果存储到内存中,存储指令并不关心数据来自哪个域。
但是,如果您将其用作长依赖链的一部分,那么如果关键路径通过被混合的数据,那么额外的指令可能是值得的,以避免混合数据的额外 2 个延迟周期混合,不是面膜。
请注意,如果掩码生成在关键路径上,则 VPSRAD 的 1 个周期延迟相当于旁路延迟延迟,因此使用 FP 混合只是掩码-> 结果的额外 1 个延迟周期链,与数据->结果链的 2 个额外周期相比。而且,如果您使用可以从 FP 或整数混合有效转发的指令来使用混合结果,那么使用 FP 混合是绝对的胜利,为相同的延迟节省指令(及其 uop)。
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, and SIMD Floating-Point Exceptions: None means that this isn't a problem. See also the x86 tag 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 完成,除非掩码生成可能偏离关键路径 and/or 相同的掩码将在关键路径上多次重复使用。)
最常见的用例是比较掩码,其中每个元素都已经是全一或全零,因此您可以与 PAND/PANDN => POR 混合。当然,只保留掩码的符号位和真值的巧妙技巧可以节省指令和延迟,特别是因为变量混合比三个布尔按位指令快一些。 (例如,ORPS 两个浮点向量以查看它们是否都是非负的,而不是 2x CMPPS 和对掩码进行 ORing。如果您不关心负零,或者您乐于将下溢处理为 -0.0
为负)。