C SIMD-AVX-具有非零值而非最高位的屏蔽

C SIMD-AVX-具有非零值而非最高位的屏蔽,c,simd,avx,C,Simd,Avx,我有AVX(没有AVX2或AVX-512)。我有一个32位值的向量(仅使用4个最低位,其余始终为零): 在内部,由于按位操作,我将向量保留为\uuuum256,位表示“浮点数”。我需要从向量中导出单个8位数字,其中包含1表示非零位,0表示零位 因此,对于上面的示例,我需要8位数字:10000110 我想到使用\u mm256\u cmp\u ps然后使用\u mm256\u movemask\u ps。然而,对于cmp,我不知道它是否能正常工作,如果数字不完全是浮点数,并且可以是任何“垃圾”。在

我有AVX(没有AVX2或AVX-512)。我有一个32位值的向量(仅使用4个最低位,其余始终为零):

在内部,由于按位操作,我将向量保留为
\uuuum256
,位表示“浮点数”。我需要从向量中导出单个8位数字,其中包含1表示非零位,0表示零位

因此,对于上面的示例,我需要8位数字:
10000110

我想到使用
\u mm256\u cmp\u ps
然后使用
\u mm256\u movemask\u ps
。然而,对于cmp,我不知道它是否能正常工作,如果数字不完全是浮点数,并且可以是任何“垃圾”。在这种情况下,cmp使用哪个操作数


或者还有其他解决方案吗?

从概念上讲,您所做的应该是可行的。高24位为零的浮点为有效浮点。然而,它们是非规范的

虽然它应该有效,但存在两个潜在问题:

  • 如果FP模式设置为将非规范化刷新为零,则它们都将被视为零。(因此,打破了这种做法)
  • 因为这些都是非规范化的,所以最终可能会受到巨大的性能损失,这取决于硬件是否能够以本机方式处理它们

  • 替代方法:

    因为上面的24位是零,所以可以对它们进行规格化。然后进行浮点比较

    (警告:未测试的代码)

    这里,
    data
    是您的输入,其中每个“float”的上24位为零。让我们调用每个8位整数
    x

    或使用
    2^23
    设置浮点的尾数,使其成为值为
    2^23+x
    的标准化浮点


    然后将
    2^23
    float
    进行比较,只有当
    x
    为非零时,才会给出1。

    备选答案,供将来有AVX2的读者参考。

    您可以强制转换到
    \uuu m256i
    并使用SIMD整数比较

    这避免了DAZ将这些小整数位模式视为零的任何问题,也避免了非规范(又称次正常)输入的微码辅助

    在某些CPU上,在
    vcmpeqd
    vpmovmskps
    之间可能有一个额外的绕过延迟周期,但您仍然领先,因为整数比较的延迟低于FP比较

    int nonzero_positions_avx2(__m256 v)
    {
        __m256i vi = _mm256_castps_si256(v);
        vi = _mm256_cmpeq_epi32(vi, _mm256_setzero_si256());
        return _mm256_movemask_ps(_mm256_castsi256_ps(vi));
    }
    

    值得注意的是,您不需要使用
    2^23
    。只要表示的底部4位为零,几乎可以使用任何其他标准化浮点值。
    int to_mask(__m256 data){
        const __m256 MASK = _mm256_set1_ps(8388608.);  //  2^23
        data = _mm256_or_ps(data, MASK);
        data = _mm256_cmp_ps(data, MASK, _CMP_NEQ_UQ);
        return _mm256_movemask_ps(data);
    }
    
    int nonzero_positions_avx2(__m256 v)
    {
        __m256i vi = _mm256_castps_si256(v);
        vi = _mm256_cmpeq_epi32(vi, _mm256_setzero_si256());
        return _mm256_movemask_ps(_mm256_castsi256_ps(vi));
    }