“计数位数设置” - 为什么这个算法?

时间:2016-10-06 10:05:05

标签: c algorithm

我在这里提到了这个算法:How does this algorithm to count the number of set bits in a 32-bit integer work?

我在上面的链接中运行了“yeer”提交的算法,因为这些算法或多或少看起来都一样。

我编写了传统的(较慢的方法,假设)代码来检查性能有多大提升。

Yeer Code:

unsigned int number=0xFFFFFFFF;

number= (number & 0x55555555) + ((number>>1) & 0x55555555);
number= (number & 0x33333333) + ((number>>2) & 0x33333333);
number= (number & 0x0F0F0F0F) + ((number>>4) & 0x0F0F0F0F);
number= (number & 0x00FF00FF) + ((number>>8) & 0x00FF00FF);
number= (number & 0x0000FFFF) + ((number>>16) & 0x0000FFFF);

printf("%d",number);

传统方式:

unsigned int number=0xFFFFFFFF;
unsigned char i=0;
unsigned char count=0;

for(i=0;i<32;i++)
{
 if((number>>i) & 1)
  count++;
}

 printf("%d",count);

第二个代码超越了“yeers”方法。

对于输入值0xFF(使用变量作为unsigned char),Traditional = 0.047s,Other = 0.063s 对于输入值0xFFFFFFFF(使用变量作为unsigned int),Traditional = 0.141s,Other = 0.141s

其他算法有什么特别之处?

我使用Codeblocks IDE来运行这两个代码。

2 个答案:

答案 0 :(得分:2)

我为不同的代码运行了一个简单的基准测试。每个片段都执行了1亿次。代码编译为gcc,没有optmization标志。每个案例都进行了几次,以确保结果不会被其他系统活动过度扭曲。重新运行时执行时间变化不到10%。

如您所见,yeer提交的算法比其他算法快得多。

测试驱动程序:

int main(int argc, char *argv[]) {
    int i, result;
    for (i= 0; i < 100000000; i++) {
        result = SWAR(i);
    }
    return 0;
}

代码1,优化的yeer代码:

int SWAR(unsigned int i) {
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

时间:0.772s

代码2,未优化的版本:

int SWAR(unsigned int number) {
    number= (number & 0x55555555) + ((number>>1) & 0x55555555);
    number= (number & 0x33333333) + ((number>>2) & 0x33333333);
    number= (number & 0x0F0F0F0F) + ((number>>4) & 0x0F0F0F0F);
    number= (number & 0x00FF00FF) + ((number>>8) & 0x00FF00FF);
    number= (number & 0x0000FFFF) + ((number>>16) & 0x0000FFFF);
    return number;
}

时间:1.241s

代码3,没有if的位计数器:

int SWAR(unsigned int number) {
  int i, count = 0;
  for(i=0;i<32;i++) {
    count += (number>>i) & 1;
  }
    return count;
}

时间:8.921s

代码4,位计数器if

int SWAR(unsigned int number) {
  int i, count = 0;
  for(i=0;i<32;i++) {
    if ((number>>i) & 1) {
        count++;
    }
  }
    return count;
}

时间:21.058s

答案 1 :(得分:1)

第一种方法没有分支,在大多数系统上将产生大约15个机器代码cpu指令(5个adda,5个shift和5个和')。

您的方法通常有128条指令(32条4条指令),即使使用预测分支,大多数cpus在错误估计循环条件时也必须至少转储一次管道,导致运行+130个cpu周期结果

我建议你尝试在随机数据上运行数百万次,你会看到差异。

尝试使用来自rand()

的数据设置的数组的1到100万的for循环

您的时间可能是其他事情,与您的代码无关,将以微秒执行