如果dim
是8的倍数,则下面的命令应该起作用(要处理余数,请在末尾添加一个琐碎的循环)。较小的API更改:
- 使用
long
代替unsigned int
作为循环索引(这有助于clang展开循环)
- 假设
bitvector
是低端的(如注释中所建议)
在循环内部,bitVector
是按字节访问的。可能值得将movemask
的2或4个结果同时进行位或一次组合(可能取决于目标体系结构)。
要计算sum
,请直接从cmp_ps
操作的结果中计算出8个部分和。由于仍然需要位掩码,因此可能值得使用popcnt
(理想情况下,将2、4或8个字节组合在一起-再次,这可能取决于您的目标体系结构)。
int process_bit_vector(uint32_t *bitVector32,float *value,const float threshold_float,const long dim) {
__m256i sum = _mm256_setzero_si256();
__m256 threshold_vector = _mm256_set1_ps(threshold_float);
uint8_t *bitVector8 = (uint8_t *)bitVector32;
for (long i = 0; i <= dim-8; i += 8) {
// compare next 8 values with threshold
// (use threshold as first operand to allow loading other operand from memory)
__m256 cmp_mask = _mm256_cmp_ps(threshold_vector,_mm256_loadu_ps(value + i),_CMP_GE_OQ);
// true values are `-1` when interpreted as integers,subtract those from `sum`
sum = _mm256_sub_epi32(sum,_mm256_castps_si256(cmp_mask));
// extract bitmask
int mask = _mm256_movemask_ps(cmp_mask);
// bitwise-or current mask with result bit-vector
*bitVector8++ |= mask;
}
// reduce 8 partial sums to a single sum and return
__m128i sum_reduced = _mm_add_epi32(_mm256_castsi256_si128(sum),_mm256_extracti128_si256(sum,1));
sum_reduced = _mm_add_epi32(sum_reduced,_mm_srli_si128(sum_reduced,8));
sum_reduced = _mm_add_epi32(sum_reduced,4));
return _mm_cvtsi128_si32(sum_reduced);
}
Godbolt-Link:https://godbolt.org/z/ABwDPe
- 出于某些原因,GCC进行了
vpsubd ymm2,ymm0,ymm1; vmovdqa ymm0,ymm2;
而不只是vpsubd ymm0,ymm1
。
- Clang无法将
load
与vcmpps
结合在一起(并且使用LE
而不是GE
比较)-如果您不关心如何处理NaN ,则可以使用_CMP_NLT_US
代替_CMP_GE_OQ
。
具有大端输出的修订版(未经测试):
int process_bit_vector(uint32_t *bitVector32,const long dim) {
int sum = 0;
__m256 threshold_vector = _mm256_set1_ps(threshold_float);
for (long i = 0; i <= dim-32; i += 32) {
// compare next 4x8 values with threshold
// (use threshold as first operand to allow loading other operand from memory)
__m256i cmp_maskA = _mm256_castps_si256(_mm256_cmp_ps(threshold_vector,_mm256_loadu_ps(value + i+ 0),_CMP_GE_OQ));
__m256i cmp_maskB = _mm256_castps_si256(_mm256_cmp_ps(threshold_vector,_mm256_loadu_ps(value + i+ 8),_CMP_GE_OQ));
__m256i cmp_maskC = _mm256_castps_si256(_mm256_cmp_ps(threshold_vector,_mm256_loadu_ps(value + i+16),_CMP_GE_OQ));
__m256i cmp_maskD = _mm256_castps_si256(_mm256_cmp_ps(threshold_vector,_mm256_loadu_ps(value + i+24),_CMP_GE_OQ));
__m256i cmp_mask = _mm256_packs_epi16(
_mm256_packs_epi16(cmp_maskA,cmp_maskB),// b7b7b6b6'b5b5b4b4'a7a7a6a6'a5a5a4a4 b3b3b2b2'b1b1b0b0'a3a3a2a2'a1a1a0a0
_mm256_packs_epi16(cmp_maskC,cmp_maskD) // d7d7d6d6'd5d5d4d4'c7c7c6c6'c5c5c4c4 d3d3d2d2'd1d1d0d0'c3c3c2c2'c1c1c0c0
); // cmp_mask = d7d6d5d4'c7c6c5c4'b7b6b5b4'a7a6a5a4 d3d2d1d0'c3c2c1c0'b3b2b1b0'a3a2a1a0
cmp_mask = _mm256_permute4x64_epi64(cmp_mask,0x8d);
// cmp_mask = [b7b6b5b4'a7a6a5a4 b3b2b1b0'a3a2a1a0 d7d6d5d4'c7c6c5c4 d3d2d1d0'c3c2c1c0]
__m256i shuff_idx = _mm256_broadcastsi128_si256(_mm_set_epi64x(0x00010203'08090a0b,0x04050607'0c0d0e0f));
cmp_mask = _mm256_shuffle_epi8(cmp_mask,shuff_idx);
// extract bitmask
uint32_t mask = _mm256_movemask_epi8(cmp_mask);
sum += _mm_popcnt_u32 (mask);
// bitwise-or current mask with result bit-vector
*bitVector32++ |= mask;
}
return sum;
}
这个想法是在应用vpmovmskb
之前先对字节进行洗牌。这需要32个输入值进行5次混洗操作(包括3个vpacksswb
),但是总和的计算是使用popcnt
而不是4个vpsubd
。可以通过在比较之前将128位半部分策略性地加载到256位向量中来避免vpermq
(_mm256_permute4x64_epi64
)。另一个想法(因为无论如何都需要重组最终结果)是将部分结果混合在一起(在我检查过的体系结构上往往需要p5
或2*p015
,所以可能不值得)。>
本文链接:https://www.f2er.com/3166600.html