AVX2:AVX 寄存器中 8 位元素的 CountTrailingZeros

Posted

技术标签:

【中文标题】AVX2:AVX 寄存器中 8 位元素的 CountTrailingZeros【英文标题】:AVX2: CountTrailingZeros on 8 bit elements in AVX register 【发布时间】:2021-12-23 18:41:42 【问题描述】:

我想要一个类似_mm256_lzcnt_epi8(__m256i a) 的函数的实现,其中对于每个 8 位元素,计算并提取尾随零的数量。

在上一个实现前导零计数的问题中,有一个使用查找表的解决方案。我想知道是否可以为此使用相同的方法。

请仅使用 AVX 和 AVX2,0 作为输入的行为可以是未定义的。

AVX2: BitScanReverse or CountLeadingZeros on 8 bit elements in AVX register

感谢您的帮助!

【问题讨论】:

【参考方案1】:
__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;

【讨论】:

【参考方案2】:

该问题中与the answer by chtz 相同的LUT 应该可以工作。

饱和技巧不起作用,但_mm256_blendv_epi8 可用于选择要使用的 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);

【讨论】:

这还不行。你能解释一下你是如何得出lookup_hilookup_lo 的数字的,所以我可以解决它吗?还请解释ctz_hictz_lo 应该包含哪些示例输入?谢谢! @simonlet,显然我忘了把setr改成set,请再试一次 @simonlet,另一个答案稍微好一点,(v)pminsb(v)pblendvb 更有效地组合结果。也许甚至值得将 clz 解决方案更改为使用 min_epu8,以遵循该模式。 我需要 256 位寄存器的函数,所以我将使用你的函数与 return _mm256_min_epu8(clz_lo, clz_hi);。谢谢!

以上是关于AVX2:AVX 寄存器中 8 位元素的 CountTrailingZeros的主要内容,如果未能解决你的问题,请参考以下文章

用于灰度到 ARGB 转换的 C++ SSE2 或 AVX2 内在函数

有没有办法用 AVX2 编写 _mm256_shldi_epi8(a,b,1) ? (向量之间每 8 位元素移位一位)

使用 AVX2 查找元素索引 - 代码优化

仅使用 avx 而不是 avx2 转置 64 位元素

优化从 AVX2 寄存器中提取 64 位值

SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器