将 __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 x86 标签 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)一起使用。
如何为 __m256
值中的所有位设置值 1?
使用 AVX 或 AVX2 内在函数?
要获得全零,您可以使用_mm256_setzero_si256()
。
为了获得全部,我目前正在使用 _mm256_set1_epi64x(-1)
,但我怀疑这比全零的情况慢。这里是否涉及内存访问或Scalar/SSE/AVX切换?
而且我似乎无法在 AVX 中找到简单的按位非操作? 如果可用,我可以简单地使用 setzero,然后是向量 NOT。
另请参阅
您显然甚至没有查看 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
.
如 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 (
编译器不喜欢 __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
.
这对于像 vxorps
这样的按位指令来说不是问题(即使助记符有 ps
,它没有来自 Skylake、IIRC 上的 FP 或 vec-int 域的旁路延迟) .
因此通常使用整数指令创建 set1(-1)
常量是安全的,因为它是 NaN,您通常不会将它与 FP 数学指令(如 mul 或 add)一起使用。