如何将 __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/