求哈希散列值用求模还是用逻辑与

哈希表

在使用key, value场景的时候,最常用的一个数据结构,能够实现O(1)时间复杂度的算法。而我们常用的算法中,一般是使用一个比目标空间略大一些的质数,通过对key做一次编码函数,得到一个整数值,再找到对应的目标空间的slot位置,进而做插入操作。如下所示:
质数不妨假设为MAX
id = fun(key),
slot_id = id % MAX
找到对应的slot位置进行查找或者插入修改操作。
注意到:这里用的是取模操作。

另一种做法

最近在读redis对应底层存储结构哈希表的时候,看到了另一种类似,但不同的做法。做法如下:采用一个2的N次减1方作为存储空间。对应的key还是用一个编码函数,获得一个id值。再通过id值,求得slot。具体如下所示:
空间大小不妨假设为MAX
id = fun(key),
slot_id = id & MAX
注意到:这里用的是逻辑与操作,获取slot_id。

有何区别

刚开始的时候,觉得没什么区别,不就是一个运算不一样么,redis何苦如此麻烦。仔细一想,不太对!对于自己一眼不能看明白的事情,必须内心保持一丝敬意,才有可能会有长进。别人故意绕个弯,肯定有他的原因,很可能是你的水平不够,redis的设计者的追求极简和效率的思想,怎么可能搞这么麻烦的事情。

分析

区别就在于逻辑运算,与除法运算的区别。
回到计算机最基本的原理。在CPU的基本原理之中,CPU根本就不认识什么加减乘除,它只认识0和1,而且只认识逻辑运算。所谓的数据运算,只不过是转化为多次逻辑运算得到的结果。能想到这一步,就说明大学的计算机组成原理没有还给老师!
因此,从理论上分析,那逻辑与的效率,肯定是要比除法的效率要高,因为逻辑运算是CPU底层就支持的运算方式 。内心感叹,这就是普通工程师,与真正优秀的工程的差距。

求证

有了理论分析,下来自己通过简单的代码验证一下自己的猜想。
以下是逻辑与运算代码

#include <stdio.h>
int main(int argc, char** argv) {
    const int MAX_LOOP = 100000000;
    int res = 0;
    for (int i = 0; i < MAX_LOOP; ++i) {
        int a = 3;
        int b = 7;
        res = a & b;
    }   
    printf("res = %d\n", res);
    return 0;
}

取模操作代码如下:

#include <stdio.h>
int main(int argc, char** argv) {
    const int MAX_LOOP = 100000000;
    int res = 0;
    for (int i = 0; i < MAX_LOOP; ++i) {
        int a = 3;
        int b = 7;
        res = a / b;
    }   
    printf("res = %d\n", res);
    return 0;
}

在我的linux测试机器上,gcc 4.8.5,CentOS Linux release 7.3.1611 (Core)
测试结果如下:
1、求模方式结果:
real 0m0.354s
user 0m0.351s
sys 0m0.001s
2、逻辑与方案结果:
real 0m0.232s
user 0m0.230s
sys 0m0.001s

从结果来看,逻辑与操作要比求模要快上不少,耗时少50%左右。

总结

从这种微小的细节看出,真正优秀的工程师,是在所有可以优化的地方,都尽可能做到最优,这才是优秀与普通水平的差距!努力前行!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值