Redis源码解析——统计二进制数中1的个数
来源:互联网 发布:内外网络切换器软件 编辑:程序博客网 时间:2024/06/06 06:34
Redis源码解析——统计二进制数中1的个数
先上源码,redisPopcount函数在bitops.c中:
size_t redisPopcount(void *s, long count) { size_t bits = 0; unsigned char *p = s; uint32_t *p4; static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; /* Count initial bytes not aligned to 32 bit. */ while((unsigned long)p & 3 && count) { bits += bitsinbyte[*p++]; count--; } /* Count bits 16 bytes at a time */ p4 = (uint32_t*)p; while(count>=16) { uint32_t aux1, aux2, aux3, aux4; aux1 = *p4++; aux2 = *p4++; aux3 = *p4++; aux4 = *p4++; count -= 16; aux1 = aux1 - ((aux1 >> 1) & 0x55555555); aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333); aux2 = aux2 - ((aux2 >> 1) & 0x55555555); aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333); aux3 = aux3 - ((aux3 >> 1) & 0x55555555); aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333); aux4 = aux4 - ((aux4 >> 1) & 0x55555555); aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333); bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + ((((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + ((((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + ((((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); } /* Count the remaining bytes. */ p = (unsigned char*)p4; while(count--) bits += bitsinbyte[*p++]; return bits;}
可以看出,Redis是根据输入的二进制数的大小选择使用不同方法统计数组中1的个数。如果二进制数的位数大于128位,即16个字节,那么首先使用variable-precision SWAR算法计算二进制数中超出128位部分中1的数量,然后再使用查表算法计算剩余部分中1的数量。
1.查表算法
(1)构建查询表bitsinbyte数组,查询表中的每一个元素表示一个8位二进制数中1的个数,按升序排列(0-255),比如bitsinbyte[0]与0x00对应,bitsinbyte[1]与0x01对应,bitsinbyte[255]与0xFF对应;
(2)将输入的二进制位数组转换为无符号字符数组(unsigned char*),然后每次取8位,作为bitsinbyte数组的下标,得到这个8位二进制数中1的个数,累加起来即得二进制数中1的个数。
2.variable-precision SWAR算法
在Redis的实现中,每循环一次,计算32*4位的二进制数中1的个数。Redis使用的是处理32位二进制位数组的variable-precision SWAR算法,算法如下,这样写容易理解一些:
aux1 = (aux1&0x55555555)+((aux>>1)&0x55555555) //步骤1aux1 = (aux1&0x33333333)+((aux>>2)&0x33333333) //步骤2aux1 = (aux1&0x0F0F0F0F)+((aux>>4)&0x0F0F0F0F) //步骤3aux1 = ((aux1*0x01010101)>>24) //步骤4
利用了归并的思想。
步骤1是计算每两位二进制数中1的个数
步骤2是计算每四位二进制数中1的个数
步骤3是计算每八位二进制数中1的个数
步骤4是将之前计算的每八位二进制数中1的个数相加,并移至最低位八位
举个实例,帮助理解:
统计0x2B4A1F87中1的个数,
经过步骤一的结果:
经过步骤二的结果:
二进制数经过步骤三的结果:
二进制数经过步骤四的结果:
aux1*0x01010101的结果:
右移24位的结果:
二进制数24~31位16~23位8~15位数0~7位0x1000000000000000000000000000010000最终统计出的结果就是16个。
- Redis源码解析——统计二进制数中1的个数
- 统计二进制数中1的个数
- 统计二进制数中1 的个数
- 统计二进制数中1的个数
- 统计二进制数中1的个数
- 统计二进制数中“1”的个数(懂二进制)
- 统计数x的二进制表示中1的个数
- 统计一个数的二进制表示中1的个数
- 统计一个二进制数中1的个数
- 统计一个数二进制表示中1的个数
- 【C语言】统计一个数二进制中1的个数
- Java统计二进制数中1的个数
- 统计二进制数中存在1的个数
- 剑指offer07--统计二进制数中1的个数
- 统计一个数二进制形式中1的个数
- (3)统计二进制数中1的个数
- 巧妙统计二进制数中1的个数
- 统计二进制数1的个数
- Maven在IntelliJ IDEA中的学习笔记(1)
- Spark组件之GraphX学习4--Structural Operators:mask
- Spark 数据ETL及部分代码示例
- MapReduce源码分析之作业Job状态机解析(一)简介与正常流程浅析
- java笔记→java线程的通讯问题(生产者与消费者)
- Redis源码解析——统计二进制数中1的个数
- Problem B
- maven/gradle 打包后自动上传到nexus仓库
- 标准MDL方法修改Page、NonPage内存的属性
- MongoDB学习12_MongoDB学习笔记之 第3章 MongoDB的Java驱动
- Android Styles 使用详解
- 小何讲进程: 阻塞进程函数 wait()和waitpid()
- 【笔记】Core GameObjects, components, and concepts relating to Unity UI development include
- Android 启动模式 launchMode