关于如何利用移位运算和逻辑运算来加速算法的小技巧

今天看到一个小技巧:

如果一个整数要做2的整数次幂的乘法或除法时,可以通过移位操作来代替,如此,可以提高执行效率。

大抵是这样的:

int i(初始化值);

则计算 i * 4时,可以等效的通过计算 i << 2 来实现,如此可以将执行的效率提升!

话虽如此,程序验证之,代码是在VS2012下编译运行的:

#include <time.h>
#include <iostream>

#define LOOP_NUM 1000000000

int main(int argc, char** argv)
{
     clock_t tStart, tEnd;
#pragma region "test multiple"
    unsigned int uiMultiplicant = 10;
    unsigned int uiMultiplier = 0x04;
    unsigned int res = 0;
    tStart = clock();
    for(int i = 0; i < LOOP_NUM; ++i)
        res = uiMultiplicant / 4; //* uiMultiplier;
    tEnd = clock();
    std::cout << double(tEnd - tStart) / CLOCKS_PER_SEC << std::endl;

    uiMultiplier = 0x07;
    tStart = clock();
    for(int i = 0; i < LOOP_NUM; ++i)
        res = uiMultiplicant >> 2;
    tEnd = clock();
    std::cout << double(tEnd - tStart) / CLOCKS_PER_SEC << std::endl;
#pragma endregion "test multiple"
}
但是经过如上的代码测试,乘(除)操作与相应的左(右)移位操作时间相当。

 

分析二者的汇编代码,发现除操作和移位操作都解释成了 shr eax, 2这样的移位操作,因此时间相当也是合情合理的了。

再看乘操作和左移操作:

    




乘操作的汇编为imul eax, ...,是单独的汇编指令,而且经过测试乘操作与相应的左移操作相比似乎效率更高!

所以从VS2012的测试环境来看,移位操作似乎并不能加速乘/除操作,但是这却能给我们一个很好的提示:

如我们要进行一项对其操作,比如要进行4的对齐,一般的写法会是这样的:

int new_len = (old_len + 3)/4 * 4;
为了加速这种运算,我们可以通过如下的技巧:

int new_len = (old_len + 3) & -4;
为何下面的代码能够起到和上面代码一样的效果呢,这就要从数值在计算机中的存储方式来说起了:

数值在计算机中都是以补码的形式存放的,那么何为补码呢?举个简单的例子,对于时钟来说相对于12点(0点),顺时针转8个个格子和逆时针转4个格子会转到相同的位置,那么相对于时钟的12个格子(模为12)来说,-8和+4是互补的。

同样对于一个8位的有符号数来说,它的模为2^7,即128,则-127相应的补码为1。计算机中正数的补码还是它本身,如同时钟一样,正方向转过4个格子就是4个格子,负数的补码为在原码基础上符号位不变,其余位取反,再在最后一位上+1,就像时钟逆转8个格子,用正转4个格子来代替一样!

利用补码的好处就是可以利用硬件的溢出原理,直接进行加操作!

知晓了如上的原理,则4在计算机中的表示为fffffffc,而不是想象中的10000004,那么如上的&操作也便顺理成章!

以上就是个小技巧,不过却可以大大提高效率!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值