CatCoding

bsfl 指令和 Bitmap 的一个优化

2012-06-20

如何找出 int 中第一个 1

对于这个问题我们可以从最原始的做法开始,如果没找到 1 返回 0,如果第一位为 1 返回 1。所以代码很简单如下:

static int first_onebit(int x){
    if(!x)
        return 0;
    else{
        int idx = 0;
        if(x%2 != 0) return 1;
        while( x%2 == 0 ) {
            x>>=1;
            idx++;
        }
        return idx+1;
    }
}

如何能做到更好呢?这里有一个 trick 使用一条指令来完成这个工作,具体可以参考Linux Kernel 里面这个 ffs的代码。我来简化一下就是这么一个函数:

/* ffs: if ret == 0 : no one bit found
   return index is begin with 1 */
static int first_onebit(int x)
{
    if (!x) {
        return -1;
    }
    else {
        int ret;
        __asm__("bsfl %1, %0; incl %0" : "=r" (ret) : "r" (x));
        return ret;
    }
}

这里的 bsfl 是一条 intel 汇编指令,它的用法是 bsfl op1,op2:顺向位扫描从右向左(从位 0–>位 15 或位 31)扫描单节字或双字节操作数 op2 中第一个含”1”的位,并把扫描到的第一个含’1’的位的位号送操作数 op1 中,所以就是一条指令完成了这个计算过程。

这里真的会有多大的差别么,我们可以用程序来计算一下,测试如下:

int main()
{
    int x;
    for(x=0; x<=1000000000; x++){
        first_onebit(x);
    }
    return 0;
}

第一个版本花费时间 15.685s,第二个版本花费时间 5.960s,而其实如果只是循环 1000000000 次什么也不做也好花费 3.091s,所以第二个版本快到如此程度。

bitmap 的优化

bitmap 是一种常用的数据结构,在编程珠玑有详细介绍,应用也比较广泛比如可以用来做操作系统当中的地址索引查询。对于 bitmap 中我们常常需要一个操作来找一个空位的 bit 来做 set 操作。既然我们知道了第一个 1 是如何快速查找的,第一 0 也就好办了,先取反,然后再找第一个 1 就行了。

#define first_zerobit(x) (first_onebit(~(x)))

继续 bitmap 的 first_empty 就优化成这样了:

u32 first_empty()
{
    u32 idx;
    for(idx=0; idx<max_idx; idx++){
        if(arr[idx] == 0xFFFFFFFF) //full
            continue;
        u32 v = arr[index];
        return 32*idx + first_zerobit(v) - 1;
    }
    return -1;
}

注意

这样的用汇编指令来优化可能会有平台差异,所以你最好清楚自己的平台是否适用。

公号同步更新,欢迎关注👻