将 __m256 值设置为全“一”位的最快方法

Fastest way to set __m256 value to all ONE bits

如何为 __m256 值中的所有位设置值 1? 使用 AVX 或 AVX2 内在函数?

要获得全零,您可以使用_mm256_setzero_si256()

为了获得全部,我目前正在使用 _mm256_set1_epi64x(-1),但我怀疑这比全零的情况慢。这里是否涉及内存访问或Scalar/SSE/AVX切换?

而且我似乎无法在 AVX 中找到简单的按位非操作? 如果可用,我可以简单地使用 setzero,然后是向量 NOT。

另请参阅 ,其中涵盖了 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 及更高版本)会对向量进行异或归零,然后 vcmpps 使用 TRUE 谓词。较旧的 clang 使用 vpcmpeqd xmm 并使用 vinsertf128 生成 128 位全一。 GCC 从内存中加载,甚至使用 -march=sandybridge.

的现代 GCC 10.1

Agner Fog's optimizing assembly guide, generating constants on the fly this way is cheap. It still takes a vector execution unit to generate the all-ones (), but it's better than any possible two-instruction sequence, and usually better than a load. See also the 标签 wiki 的矢量部分所述。

编译器不喜欢 ,即使是那些可以通过简单的移位从全一生成的编译器。即使您尝试通过编写 __m128i float_signbit_mask = _mm_srli_epi32(_mm_set1_epi16(-1), 1),编译器通常会执行常量传播并将向量放入内存中。这让他们可以在以后使用时将其折叠成内存操作数,以防没有循环来提升常量。


And I can't seem to find a simple bitwise NOT operation in AVX?

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


FP 与整数指令和绕过延迟

Intel CPU(至少是 Skylake)有一个奇怪的效果,即 SIMD-integer 和 SIMD-FP 之间的额外旁路延迟在生成寄存器的 uop 执行后很长时间仍然发生。例如vmulps ymm1, ymm2, ymm0 如果 ymm0 是由 vpcmpeqd 生成的,则 ymm2 -> ymm1 关键路径可能会有一个额外的延迟周期。如果您不以其他方式覆盖 ymm0.

,这将持续到下一个上下文切换恢复 FP 状态

这对于像 vxorps 这样的按位指令来说不是问题(即使助记符有 ps,它没有来自 Skylake、IIRC 上的 FP 或 vec-int 域的旁路延迟) .

因此通常使用整数指令创建 set1(-1) 常量是安全的,因为它是 NaN,您通常不会将它与 FP 数学指令(如 mul 或 add)一起使用。