将位向量转换为一位

时间:2013-11-24 08:38:59

标签: performance assembly x86 bit-manipulation operations

对于非零无符号整数值,是否有一种有效的方法可以获得0x00000001或0xFFFFFFFF,而在没有分支的情况下,有0可以获得零值吗?

我想测试几个蒙版并根据它创建另一个蒙版。基本上,我想优化以下代码:

unsigned getMask(unsigned x, unsigned masks[4])
{
    return (x & masks[0] ? 1 : 0) | (x & masks[1] ? 2 : 0) |
           (x & masks[2] ? 4 : 0) | (x & masks[3] ? 8 : 0);
}

我知道有些优化编译器可以处理这个问题,但即使是这样,他们究竟是如何做到的呢?我查看了 Bit twiddling hacks 页面,但发现只有使用布尔条件的条件设置/清除掩码的描述,因此从intbool的转换应该在方法之外完成。

如果没有通用的方法来解决这个问题,我怎样才能有效地使用x86汇编程序代码呢?

3 个答案:

答案 0 :(得分:2)

x86 SSE2只需几条指令即可完成此操作,最重要的是movmskps,它将SIMD向量的每个4字节元素的高位提取为整数位图。

Intel's intrinsics guide很好,另请参阅SSE tag wiki

#include <immintrin.h>

static inline
unsigned getMask(unsigned x, unsigned masks[4])
{
    __m128i vx = _mm_set1_epi32(x);
    __m128i vm = _mm_load_si128(masks);    // or loadu if this can inline where masks[] isn't aligned

    __m128i and = _mm_and_si128(vx, vm);

    __m128i eqzero = _mm_cmpeq_epi32(and, _mm_setzero_si128());   // vector of 0 or -1 elems
    unsigned zeromask = _mm_movemask_ps(_mm_castsi128_ps(eqzero));
    return zeromask ^ 0xf;  // flip the low 4 bits
}

直到AVX512,没有SIMD cmpneq,因此最好的选择是提取掩码后进行标量XOR。 (我们只想翻转低4位,而不是全部用NOT翻转。)

答案 1 :(得分:1)

在x86中执行此操作的常用方法是:

test eax, eax
setne al

答案 2 :(得分:1)

您可以使用!! to coerce a value to 0 or 1并像这样重写表达式

return !!(x & masks[0]) | (!!(x & masks[1]) << 1) |
       (!!(x & masks[2]) << 2) | (!!(x & masks[3]) << 3);