AVX2:AVX 寄存器中 8 位元素上的 CountTrailingZeros
AVX2: CountTrailingZeros on 8 bit elements in AVX register
我想要一个像 _mm256_lzcnt_epi8(__m256i a)
这样的函数的实现,其中对于每个 8 位元素,计算并提取尾随零的数量。
在上一个实现前导零计数的问题中,有一个使用查找的解决方案 table。我想知道是否有人可以使用相同的方法。
只有 AVX 和 AVX2,并且 0
作为输入的行为可以是未定义的。
感谢您的帮助!
与该问题中 相同的 LUT 应该可以工作。
饱和度技巧不起作用,但 _mm256_blendv_epi8
可用于 select 使用哪个 LUT 结果。
低 LUT 是值 0..15 的答案,对于 0,通过 blendv 在另一个 LUT 中看到的是 0xFF。
像这样(未测试):
__m256i ctz_epu8(__m256i values)
{
// extract upper nibble:
__m256i hi = _mm256_and_si256(_mm256_srli_epi16(values, 4), _mm256_set1_epi8(0xf));
// extract lower nibble:
__m256i lo = _mm256_and_si256(values, _mm256_set1_epi8(0xf));
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
const __m256i lookup_hi = _mm256_broadcastsi128_si256(_mm_set_epi8(4, 5, 4, 6, 4, 5, 4, 7, 4, 5, 4, 6, 4, 5, 4, 8));
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
const __m256i lookup_lo = _mm256_broadcastsi128_si256(_mm_set_epi8(0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0xFF));
// look up each half
__m256i ctz_hi = _mm256_shuffle_epi8(lookup_hi, hi);
__m256i ctz_lo = _mm256_shuffle_epi8(lookup_lo, lo);
// combine results
return _mm256_blendv_epi8(ctz_lo, ctz_hi, ctz_lo);
}
__m128i ssse3_tzcnt_epi8(__m128i v) {
const __m128i lut_lo = _mm_set_epi8(0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8);
const __m128i lut_hi = _mm_set_epi8(4, 5, 4, 6, 4, 5, 4, 7, 4, 5, 4, 6, 4, 5, 4, 8);
const __m128i nibble_mask = _mm_set1_epi8(0x0F);
__m128i t;
t = _mm_and_si128(nibble_mask, v);
v = _mm_and_si128(_mm_srli_epi16(v, 4), nibble_mask);
t = _mm_shuffle_epi8(lut_lo, t);
v = _mm_shuffle_epi8(lut_hi, v);
v = _mm_min_epu8(v, t);
return v;
}
我想要一个像 _mm256_lzcnt_epi8(__m256i a)
这样的函数的实现,其中对于每个 8 位元素,计算并提取尾随零的数量。
在上一个实现前导零计数的问题中,有一个使用查找的解决方案 table。我想知道是否有人可以使用相同的方法。
只有 AVX 和 AVX2,并且 0
作为输入的行为可以是未定义的。
感谢您的帮助!
与该问题中
饱和度技巧不起作用,但 _mm256_blendv_epi8
可用于 select 使用哪个 LUT 结果。
低 LUT 是值 0..15 的答案,对于 0,通过 blendv 在另一个 LUT 中看到的是 0xFF。
像这样(未测试):
__m256i ctz_epu8(__m256i values)
{
// extract upper nibble:
__m256i hi = _mm256_and_si256(_mm256_srli_epi16(values, 4), _mm256_set1_epi8(0xf));
// extract lower nibble:
__m256i lo = _mm256_and_si256(values, _mm256_set1_epi8(0xf));
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
const __m256i lookup_hi = _mm256_broadcastsi128_si256(_mm_set_epi8(4, 5, 4, 6, 4, 5, 4, 7, 4, 5, 4, 6, 4, 5, 4, 8));
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
const __m256i lookup_lo = _mm256_broadcastsi128_si256(_mm_set_epi8(0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0xFF));
// look up each half
__m256i ctz_hi = _mm256_shuffle_epi8(lookup_hi, hi);
__m256i ctz_lo = _mm256_shuffle_epi8(lookup_lo, lo);
// combine results
return _mm256_blendv_epi8(ctz_lo, ctz_hi, ctz_lo);
}
__m128i ssse3_tzcnt_epi8(__m128i v) {
const __m128i lut_lo = _mm_set_epi8(0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8);
const __m128i lut_hi = _mm_set_epi8(4, 5, 4, 6, 4, 5, 4, 7, 4, 5, 4, 6, 4, 5, 4, 8);
const __m128i nibble_mask = _mm_set1_epi8(0x0F);
__m128i t;
t = _mm_and_si128(nibble_mask, v);
v = _mm_and_si128(_mm_srli_epi16(v, 4), nibble_mask);
t = _mm_shuffle_epi8(lut_lo, t);
v = _mm_shuffle_epi8(lut_hi, v);
v = _mm_min_epu8(v, t);
return v;
}