使用位与运算代替取模运算

 

使用构造哈希表的方法来代替简单的遍历查找

是常用的算法优化

根据关键字计算哈希值的时候

通常使用取模运算计算最终的桶下标

以防止桶的溢出

在Linux内核中也使用了大量的哈希表进行查找

起初内核中也是使用取模的方式计算桶下标

不过现在内核中在实现哈希表的时候

桶通常选择为2^n个

使用按位与(2^n - 1)的方式计算桶下标

最终的目的都是为了确定桶下标

但是内核为什么选择位与的方式还是值得考究的

使用一个小程序andmod.c来看看位与和取模的区别

 

int
main(void)
{
        int a = 0x11;
        int b = 0x22;
        int c = 0x33;
        int d = 0x44;

        c = a & b;
        d = a % b;

        return 0;
}

 

直接使用gcc -o andmod andmod.c来编译这段小程序

不要使用-O选项进行优化

因为这段代码本质上是无用的

会被编译器优化掉

 

然后使用objdump -d andmod来查看汇编代码

程序中使用一些诸如0x11这样的特殊值有助于对反汇编出的代码进行分析

重点查看main函数这段就可以了

 

08048394 <main>:
 8048394:        55                                     push   %ebp
 8048395:        89 e5                               mov    %esp,%ebp
 8048397:        83 ec 10                          sub    $0x10,%esp
 804839a:        c7 45 f0 11 00 00 00    movl   $0x11,-0x10(%ebp)               # 局部变量 a = 0x11
 80483a1:        c7 45 f4 22 00 00 00    movl   $0x22,-0xc(%ebp)                 # b = 0x22
 80483a8:        c7 45 f8 33 00 00 00    movl   $0x33,-0x8(%ebp)                 # c = 0x33
 80483af:         c7 45 fc 44 00 00 00     movl   $0x44,-0x4(%ebp)                # d = 0x44
 80483b6:        8b 45 f4                          mov    -0xc(%ebp),%eax                  # b存入寄存器eax
 80483b9:        8b 55 f0                          mov    -0x10(%ebp),%edx               # a存入寄存器edx
 80483bc:        21 d0                               and    %edx,%eax                            # 位与运算,结果在寄存器eax内
 80483be:        89 45 f8                          mov    %eax,-0x8(%ebp)                # 结果存入c
 80483c1:        8b 45 f0                           mov    -0x10(%ebp),%eax              # 构造被除数,a存入寄存器eax
 80483c4:        89 c2                                mov    %eax,%edx                          # a存入寄存器edx
 80483c6:        c1 fa 1f                             sar    $0x1f,%edx                            # 算术右移31位
 80483c9:        f7 7d f4                             idivl  -0xc(%ebp)                             # 除数b,被除数为[%edx][%eax]
 80483cc:        89 55 fc                            mov    %edx,-0x4(%ebp)               # 余数存入d,即取模运算的结果
 80483cf:         b8 00 00 00 00               mov    $0x0,%eax                            # 返回值0存入寄存器eax
 80483d4:       c9                                      leave 
 80483d5:       c3                                      ret   

 

可以看到位与使用and指令计算

而取模是通过除法运算指令idivl(这里是有符号32位除法)

取其余数来计算的

根据网上的一个指令周期资料Coding_ASM_-_Intel_Instruction_Set_Codes_and_Cycles.pdf

以表中Pentium的数据为例

 

位与计算使用了2次mov指令和1次and指令

共需3个CPU周期

 

取模运算使用了2次mov指令、1次sar指令和1次idivl指令

共需52个CPU周期

 

当然这个指令周期表也只是参考而已

至于不同的机器不同的编译器优化后差距多少

就要具体问题具体分析了

如此一看内核中使用位与运算代替取模运算的原因不言自明

节约了CPU周期提升了整体性能

当哈希表的查找中桶下标计算很频繁的时候

由此节约的CPU周期还是很可观的

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值