如果您想避免 LUT 和/或存储转发停顿,您可以这样做来设置
avx-256 寄存器的第 k 位:
inline __m256i setbit_256(__m256i x,int k){
// constants that will (hopefully) be hoisted out of a loop after inlining
__m256i indices = _mm256_set_epi32(224,192,160,128,96,64,32,0);
__m256i one = _mm256_set1_epi32(-1);
one = _mm256_srli_epi32(one, 31); // set1(0x1)
__m256i kvec = _mm256_set1_epi32(k);
// if 0<=k<=255 then kvec-indices has exactly one element with a value between 0 and 31
__m256i shiftcounts = _mm256_sub_epi32(kvec, indices);
__m256i kbit = _mm256_sllv_epi32(one, shiftcounts); // shift counts outside 0..31 shift the bit out of the element
// kth bit set, all 255 other bits zero.
return _mm256_or_si256(kbit, x); // use _mm256_andnot_si256 to unset the k-th bit
}
以下是我之前的答案,它不那么直截了当,现在已经过时了。
#include <immintrin.h>
inline __m256i setbit_256(__m256i x,int k){
__m256i c1, c2, c3;
__m256i t, y, msk;
// constants that will (hopefully) be hoisted out of a loop after inlining
c1=_mm256_set_epi32(7,6,5,4,3,2,1,0);
c2=_mm256_set1_epi32(-1);
c3=_mm256_srli_epi32(c2,27); // set1(0x1f) mask for the shift within elements
c2=_mm256_srli_epi32(c2,31); // set1(0x1)
// create a vector with the kth bit set
t=_mm256_set1_epi32(k);
y=_mm256_and_si256(c3,t); // shift count % 32: distance within each elem
y=_mm256_sllv_epi32(c2,y); // set1( 1<<(k%32) )
t=_mm256_srli_epi32(t,5); // set1( k>>5 )
msk=_mm256_cmpeq_epi32(t,c1); // all-ones in the selected element
y=_mm256_and_si256(y,msk); // kth bit set, all 255 other bits zero.
x=_mm256_or_si256(y,x); /* use _mm256_andnot_si256 to unset the k-th bit */
return x;
}
我不确定这是否会比其他答案中建议的方法更快。
考虑到常量将被提升出循环,这可以使用 clang 或 gcc (Godbolt compiler explorer) 编译为非常好的 asm。像往常一样,clang 阻止了动态生成常量的尝试,并从内存中广播加载它们(这在现代 CPU 上非常有效)。