当前位置:C++技术网 > 资讯 > 高!最全面经典的轮流策略实现技巧

高!最全面经典的轮流策略实现技巧

更新时间:2015-06-27 01:04:41浏览次数:1+次

    为了实现双缓冲机制,需要在内存使用两个数组,然后轮流切换使用。因为此轮流切换在循环内部执行,而循环会涉及大量的循环,因此在循环中对效率是非常注重的。稍有疏忽,性能大打折扣。
而实现双缓冲,则需要使用两个数组。在循环中,先操作第一给数组,然后切换到第二个数组,这样就随着循环的索引不断的递增,可以不断的切换。这里只讨论这个轮转模型,双缓冲就是典型的两个元素的轮转实现,外加一些同步控制而已。
我们对于此类问题,通常会想到,用一个布尔变量模型来做两个元素的切换。我们先只是讨论两个元素的。然后再加以扩展。

    对于初学者或者经验不足的朋友来讲,一开始想到的是用布尔变量,比如代码如下:

bool bIsDo = false;
for(int i=0;i<1000000000;i++)
{
    if(bIsDo == true)bIsDo = !bIsDo;
    //后面是其他操作
    ......
}

    再有一点基础的就是稍微调整一下代码,说明他注意到一个编程代码习惯,防出错:

bool bIsDo = false;
for(int i=0;i<1000000000;i++)
{
    if(true == bIsDo)bIsDo = !bIsDo;
    //后面是其他操作
    ......
}

    然后再有一点经验的人,理解稍微深一些,就简化了代码。因为布尔值本来就是对错,加入==判断,多此一举。然而对于很多初学者,为了一眼看去是什么意思,就加了==判断。代码如下:

bool bIsDo = false;
for(int i=0;i<1000000000;i++)
{
    if(bIsDo)bIsDo = !bIsDo;
    //后面是其他操作
    ......
}

    这样写语法确实没有错。我们不能说什么。然而有时候对于效率来讲,是非常重要的。因此,上面的代码看上去没什么,实则对效率大打折扣。没经验的人也想不出能如何改进了。
    而这种轮转的策略,就是状态的反转,仅此而已。因为这个是两个元素的,不需要其他的考虑。这样的反转代码也很简洁。然而有些装逼的写出了如下代码:

int IsDo = 0;
for(int i=0;i<1000000000;i++)
{
    if(IsDo)
        IsDo = 0;
    else
        IsDo = 1;
    //后面是其他操作
    ......
}

    有些人确实这么写过,不过装逼不成,悲剧了,被人家喷一大堆。越搞越麻烦,不安分有没有本事。还是回去好好学学基础。他偏偏不用布尔值,说要考虑以后可能会轮转多个元素。但是既然你这只有两个,就不要考虑之后的。把握当下,把现在的做好。如果讲究高效,讲究多个轮转,自然还有其他的方法。所以,两个元素的,使用布尔值来处理,就很好。如果是多个元素,则再想其他办法。后面我们会给出一些参考实现。
    首先来看看这些效率如何?因为是大量的循环,每一个细微的性能损失都会放大很多倍,就如果中国这么多人,每一个小问题,都是大问题,这是某领导说过的一句。程序里也是一样,而且很明显,很及时的表现出来。
    学习计算机基础理论课程后,你会发现,有一个空间局部性和时间局部性,都是为了提高执行效率的。指的是代码的执行一般按照顺序依次执行以及存储的数据一般都是连续存储。因此,连续存储和顺序执行,就可以高效实现预读取。这是提高效率的底层技术理论支持。
    预读取指的是,因为程序指令是顺序执行,那么因此现在CPU可以是多单元的,可以同时读取,或者计算和读写内存可以同时进行,这样就可以同时做很多事情,就可以很好的提高效率。那么如果是顺序执行的代码和连续存储的数据,那么就可以将即将要执行的代码或者数据块提前载入到高速缓存,然后CPU就直接在高速缓存中取数据,这样极大提高了性能。
    这些是计算机组成原理的基础内容,如果感兴趣,可以去阅读相关书籍。不过这里大致也讲了这些内容,可以帮助深入理解。
    然而,在代码中,最终生成的就是指令,不管编译器如何优化,整体的流程始终如你的代码差不多。那么程序的代码的写法,就直接决定了执行的效率,决定了程序的性能。所以,不要以为,程序的效率与你无关。不经意间,你就学会了一个狠招。
    遵从以上的原理,在循环中,大量的执行一些操作,对性能非常的注重,因为CPU执行速度极快,所以,效果很明显。这个不比程序里偶尔才执行的简单操作。简单的操作倒是无所谓,对于这种巨量循环,不可不注重。对于这些效率的思考,可以提高你的编码水平。
    与我们这里讨论的重要的正是代码的顺序执行特性,而局部存储就自己查阅资料吧。代码的顺序执行,硬件可以有很多优化,从而提高速度。但是如果在巨量循环里,出现了条件判断,跳转,往往都是效率的杀手。每一次判断,都会带有跳转,跳转之后,就会导致预读取的指令作废。每一次都如此,相当的损耗效率。对于判断也是增加了消耗。如果你了解汇编,就会看到这些代码生成的汇编指令,这样也就更加清晰。不过,了解到这里,就差不多了。
    之前的代码是对状态的反转,如果要实现双缓冲切换,还要加上一句代码,代码如下:

bool bIsDo = false;
int buf[2]={0};
for(int i=0;i<1000000000;i++)
{
    if(true == bIsDo)bIsDo = !bIsDo;
    buf[bIsDo]=0;// - 这里假设赋值为0,具体操作看程序的功能
    //后面是其他操作
    ......
}

    如果要实现多个元素的轮转,那么就可以使用取模来完成,这也用不着前面装逼的代码的写法。效率还很高。当然这个也适合两个元素的。效率比之前的高。看出来了吧。没有逻辑判断了。这个也是需要有一点经验的哦。代码如下:

int buf[3]={0};
for(int i=0;i<1000000000;i++)
{
    buf[i%3]=0;
    //后面是其他操作
    ......
}


    所以,为了提高效率,而改进上面的代码。对于计算机来说,直接的位操作,效率是极高的。这是计算机的专长。所以,我们就使用位操作来改进程序。改进的代码如下:

int buf[2]={0};
for(int i=0;i<1000000000;i++)
{
    buf[i & 0x01]=0;
    //后面是其他操作
    ......
}

    这里对两个元素的轮转切换有显著的性能提升。使用的原理就是用最低一位作为一个开关,而i的自增会触发最低一位的不停的反转,正好满足了两个元素的切换,这样就省去了判断,且很高效的实现了轮转。这一招,目前看来是最高的。
    然而位操作实现多个元素的轮转,依次类推,我们只需要扩展一位,即用两位,就支持4个元素的轮转,如果要更多,可以扩展更多的位。以二的次方来计算哦。
    到此为止,对于轮转策略,基本上由入门级到高手级的实现方法和代码都给出了。总结一下,最入门的是基本的条件判断,这个效率很低,但是功能可以实现。然后就是代码的一些改进。条件判断要扩展轮流的元素,变得麻烦,效率更低。元素越多,要判断的次数越多。所以,进一步的提升就是使用取模,这样用一个轻松的模型实现高效的轮转。然而为了更进一步提高性能,达到极致,就用上了位操作。位操作轮转从两个元素到多个元素的扩展,都讲明白了。