__m256
值中将所有位设置为 1?
使用 AVX 或 AVX2 intrinsics?要获得所有零,可以使用
_mm256_setzero_si256()
。要获得所有的 1,我目前使用的是
_mm256_set1_epi64x(-1)
,但我怀疑这比全零情况要慢。这里是否涉及到内存访问或标量/SSE/AVX切换?而且我似乎找不到一个简单的位 NOT 操作在 AVX 中?如果有的话,我可以简单地使用 setzero,然后是一个向量 NOT。
__m256
值中将所有位设置为 1?
使用 AVX 或 AVX2 intrinsics?_mm256_setzero_si256()
。_mm256_set1_epi64x(-1)
,但我怀疑这比全零情况要慢。这里是否涉及到内存访问或标量/SSE/AVX切换?请参见高效地将CPU寄存器中的所有位设置为1,该文章涵盖了AVX、AVX2和AVX512 zmm和k(mask)寄存器。
显然你甚至没有查看汇编输出,这是很容易做到的:
#include <immintrin.h>
__m256i all_ones(void) { return _mm256_set1_epi64x(-1); }
编译使用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
最好使用冷寄存器来输入以减轻假依赖。
最近的clang(5.0及以上版本)在AVX2不可用时对向量进行异或零操作,然后使用TRUE谓词进行vcmpps
。旧版本的clang使用vpcmpeqd xmm
生成128位全为1,然后使用vinsertf128
。GCC从内存加载,即使是带有-march=sandybridge
的现代GCC 10.1也是如此。
正如Agner Fog的优化汇编指南中向量部分所描述的那样,以这种方式动态生成常数是比较便宜的。虽然仍需要一个向量执行单元来生成全1(不同于_mm_setzero
),但它比任何可能的两条指令序列都要好,通常也比加载更好。另请参见x86标签wiki。
__m128i float_signbit_mask = _mm_srli_epi32(_mm_set1_epi16(-1), 1)
编写,编译器通常也会进行常量传播并将向量放入内存。这使得它们可以在后续使用中将其折叠为内存操作数,在没有循环来提升常量的情况下。
vxorps
(_mm256_xor_ps
) 与全1进行异或操作来实现。不幸的是,SSE/AVX没有提供一种无需向量常量进行NOT操作的方法。
FP vs 整数指令和旁路延迟
英特尔 CPU(至少 Skylake)存在一种奇怪的效应,即在 SIMD-整数和 SIMD-FP 之间的额外旁路延迟仍会在产生寄存器的 uop 执行后很长时间发生。例如,如果 ymm0
是由 vpcmpeqd
产生的,则 vmulps ymm1, ymm2, ymm0
的关键路径 ymm2
-> ymm1
可能会有一个额外的时钟周期的延迟。 如果您不覆盖 ymm0
,则此延迟会持续到下一个上下文切换恢复 FP 状态。
对于像 vxorps
这样的位操作指令来说,这不是问题(即使助记符中有 ps
,在 Skylake 上它也没有来自 FP 或 vec-int 域的旁路延迟,如果我没记错的话)。
因此,通常可以使用整数指令创建 set1(-1)
常量,因为它是 NaN,并且您通常不会将其与 FP 数学指令(如 mul 或 add)一起使用。
vpcmpeqd
。Clang似乎会将_mm256_set1_epi64x(-1);
优化为与_mm256_cmpeq_epi64(_mm256_setzero_si256(), _mm256_setzero_si256());
相同的操作。 - Dan Mašek