如何将 __m256 值中的所有位都设置为 1?
使用 AVX 或 AVX2 内在函数?
要获得全零,您可以使用 _mm256_setzero_si256()
为了得到所有的,我目前正在使用 _mm256_set1_epi64x(-1) ,但我怀疑这比全零的情况慢。这里是否涉及内存访问或标量/SSE/AVX 切换?
而且我似乎在 AVX 中找不到简单的按位非操作?
如果可用,我可以简单地使用 setzero,后跟一个向量 NOT。

最佳答案

另请参阅 Set all bits in CPU register to 1 efficiently,其中涵盖了 AVX、AVX2 和 AVX512 zmm 和 k(掩码)寄存器。

你显然甚至没有看 asm 输出,这是微不足道的:

#include <immintrin.h>
__m256i all_ones(void) { return _mm256_set1_epi64x(-1); }
compiles to 与 GCC 和 clang 与任何包含 AVX2 的 -march
    vpcmpeqd        ymm0, ymm0, ymm0
    ret
要获得 __m256 (不是 __m256i ),您可以只转换结果:
  __m256 nans = _mm256_castsi256_ps( _mm256_set1_epi32(-1) );
如果没有 AVX2,一个可能的选项是 vcmptrueps dst, ymm0,ymm0 最好带有一个冷寄存器用于输入以减轻错误依赖。
如果 AVX2 不可用,最近的 clang(5.0 及更高版本)会对向量执行异或零,然后使用 TRUE 谓词 vcmpps。较旧的 clang 使用 vpcmpeqd xmm 制作 128 位全 1 并使用 vinsertf128 。 GCC 从内存中加载,甚至是带有 -march=sandybridge 的现代 GCC 10.1。

正如 Agner Fog's optimizing assembly guide 的向量部分所描述的那样,以这种方式动态生成常量很便宜。它仍然需要一个向量执行单元来生成全 1 ( unlike _mm_setzero ),但它比任何可能的两个指令序列都要好,通常比加载更好。另请参阅 x86 标签维基。
编译器不喜欢 generate more complex constants on the fly ,即使是那些可以通过简单的转变从全 1 生成的。即使您尝试编写 __m128i float_signbit_mask = _mm_srli_epi32(_mm_set1_epi16(-1), 1) ,编译器通常也会进行常量传播并将向量放入内存中。这让他们可以将它折叠成一个内存操作数,以便稍后在没有循环将常量提升出来的情况下使用。


您可以通过使用 vxorps ( _mm256_xor_ps ) 与全 1 进行异或来做到这一点。不幸的是,SSE/AVX 不提供一种在没有向量常量的情况下执行 NOT 的方法。

FP vs 整数指令和旁路延迟
Intel CPU(至少 Skylake)有一个奇怪的效果,其中 SIMD-integer 和 SIMD-FP 之间的额外旁路延迟在生成寄存器的 uop 执行后很长时间仍然发生。例如如果 vmulps ymm1, ymm2, ymm0 是由 ymm2 生成的,那么 ymm1 可能会对 ymm0 -> vpcmpeqd 关键路径有一个额外的延迟周期。如果您不以其他方式覆盖 ymm0 ,这将持续到下一个上下文切换恢复 FP 状态。
这对于像 vxorps 这样的按位指令不是问题(即使助记符具有 ps ,它也没有来自 Skylake、IIRC 上的 FP 或 vec-int 域的旁路延迟)。
因此,通常使用整数指令创建 set1(-1) 常量是安全的,因为这是一个 NaN,您通常不会将它与 mul 或 add 等 FP 数学指令一起使用。

关于bit-manipulation - 将 __m256 值设置为所有 ONE 位的最快方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37469930/

10-10 06:08