计算AVX2向量中每个元素的前导零位,模拟_mm256_lzcnt_epi32

tml*_*len 6 bit-manipulation simd avx avx2 avx512

使用AVX512,有一个internal函数_mm256_lzcnt_epi32,该函数返回一个向量,该向量对于8个32位元素中的每个元素,包含输入向量元素中前导零位的数量。

是否有仅使用AVX和AVX2指令来实现此目标的有效方法?

目前,我正在使用一个循环,该循环提取每个元素并应用该_lzcnt_u32函数。


相关:要对一个大位图进行位扫描,请参见__m256i字中的前导零计数,该字使用pmovmskb->位扫描以找到要执行标量位扫描的字节。

这个问题是关于当您实际上要使用全部8个结果而不仅仅是选择一个时,对8个单独的32位元素执行8个单独的lzcnts。

aqr*_*rit 5

float 代表指数格式的数字,因此int-> FP转换为我们提供了在指数字段中编码的最高置位位的位置。

我们希望int- > float震级四舍五入下降(截断向0值),最近的不是默认四舍五入。那可能会四舍五入,0x3FFFFFFF看起来像0x40000000。如果您在不进行任何FP数学运算的情况下进行了大量此类转换,则可以将MXCSR 1中的舍入模式设置为截断,然后在完成后将其设置回去。

否则,您可以v & ~(v>>8)用来保留8个最高有效位,并使某些或所有较低位保持为零,包括MSB下方的一个可能置位的位8。这足以确保所有舍入模式都不会舍入到2的下一个幂。它始终保持8个最高有效位,因为v>>8移位了8个零,所以倒数为8个。在低位位置,无论MSB处于什么位置,都会从高位开始经过8个零,因此它将永远不会清除任何整数的最高有效位。根据MSB队列下方的置位方式,它可能会或可能不会清除以下8个最高有效位。

转换后,我们在位模式上使用整数移位将指数(和符号位)移至底部,并使用饱和减法消除偏置。min如果在原始32位输入中未设置任何位,则我们将结果设置为32。

__m256i avx2_lzcnt_epi32 (__m256i v) {
    // prevent value from being rounded up to the next power of two
    v = _mm256_andnot_si256(_mm256_srli_epi32(v, 8), v); // keep 8 MSB

    v = _mm256_castps_si256(_mm256_cvtepi32_ps(v)); // convert an integer to float
    v = _mm256_srli_epi32(v, 23); // shift down the exponent
    v = _mm256_subs_epu16(_mm256_set1_epi32(158), v); // undo bias
    v = _mm256_min_epi16(v, _mm256_set1_epi32(32)); // clamp at 32

    return v;
}
Run Code Online (Sandbox Code Playgroud)

脚注1:fp-> int转换可用于截断(cvtt),但int-> fp转换仅可用于默认舍入(取决于MXCSR)。

AVX512F为512位向量引入了舍入模式替代,可以解决此问题__m512 _mm512_cvt_roundepi32_ps( __m512i a, int r);。但是所有带有AVX512F的CPU也都支持AVX512CD,因此您可以使用_mm512_lzcnt_epi32。加上AVX512VL,_mm256_lzcnt_epi32

  • 从概念上讲,``clz(uint32_t a)=(a)?(158-(float_as_uint32(uint32_to_float_rz(a))>> 23)):32`。如果一个人使用“ uint32_t”到“ float”的常规转换且舍入为最接近值,则结果可能会舍入为2的下一个幂,从而导致错误的clz计数。因此,转换时需要四舍五入为零(“ rz”)。不过,不确定如何在AVX中最好地执行此转换。 (2认同)