if-else分支预测降低执行效率

f27c18f992c8d816381348af8b32420e.png

分支预测的英文名字是「Branch Prediction」,分支预测如何工作的,为什么影响执行效率?

8666a71eab45e28bd14aa041978e388b.png

分支预测对程序的影响

分析如下代码:

#include <algorithm>
#include <ctime>
#include <iostream>
int main()
{
  const unsigned arraySize = 32768;
  int data[arraySize];
  for (unsigned c = 0; c < arraySize; ++c)
      data[c] = std::rand() % 256;            // ---------------------------------------------------------           //With this, the next loop runs faster.    // std::sort(data, data + arraySize); //[here]
           // ---------------------------------------------------------
  clock_t start = clock();
  long long sum = 0;
  for (unsigned i = 0; i < 100000; ++i) {
      for (unsigned c = 0; c < arraySize; ++c) { // Primary loop
          if (data[c] >= 128) sum += data[c];
      }
  }
  double elapsedTime = static_cast<double>(clock()-start)/CLOCKS_PER_SEC;
  std::cout << elapsedTime << '\n';
  std::cout << "sum = " << sum << '\n';
}

未开启sort排序,执行结果:

@ubuntu:/data/study$ g++ fenzhi.cpp && ./a.out
21.6046
sum = 314931600000

开启sort排序,执行结果:

@ubuntu:/data/study$ g++ fenzhi.cpp && ./a.out
8.52157
sum = 314931600000

可见,data数组排序后执行的时间上发生了非常大的变化。

7f6efb3ec1f911aabebc0555364ffb13.png

所以,他们发生了什么事情呢?

导致结果不同的原因就是分支预测,分支预测是CPU处理器对程序的一种预测,和CPU架构有关系,现在的很多处理器都有分支预测的功能。

CPU在执行这段代码的时候

if (data[c] >= 128) sum += data[c];

CPU会有一个提前预测机制,比如前面的执行结果都是true,那么下一次在判断if的时候,就会假设是true来处理,让下面的几条指令提前进入预装。当然,这个判断不会影响实际的结果,只是为了让CPU并行执行代码。

CPU执行一条指令分为四个阶段:

46302114536c144822969e51e5f8a868.png

分阶段执行,也就是常说的pipeline(流水线执行)。先给出一个预测结果,让流水线直接执行,执行对了则继续;执行错了,则退回去重新执行,直到对为止。

例如一段代码如下:

int a = 0;
a += 1;
a += 2;
a += 3;

37caf505c1016611fa074237784e7f33.png

CPU并不是运行完a = 0结束后,才开始执行a+=1,实际CPU是在运行a=0的第一条读执行后,马上就去运行a+=1的读执行了。这样按流水线执行,速度上得到了大幅度的提升。

但是,对于if 语句,存在分支预测的情况

063f475be5185bf08831ddb96e63bf77.png

通过比较我们可以发现,如果存在分支预测的时候,就让执行速度变快了。如果预测失败,也会影响执行的时间。

在前面的例子中,没有对数组排序的情况下,分支预测大概率会失败,这会导致指令执行结束后重新取下一条指令执行,无法发挥流水线的效果,严重影响执行效率。

而排序后的例子中,分支预测一直处于成功的状态,CPU的执行速率得到大幅度地提升。

7cd19dd1cbe6c608d1d17aafc576f5d3.png

如何解决分支预测引起的性能下降

分支预测存在一定的能性下降,想让性能提升的方法就是尽量少用if/else影响判断。上面的代码可以修改成这样,数据减去128后为负数则运算后加0:

#include <algorithm>
#include <ctime>
#include <iostream>
int main()
{   const unsigned arraySize = 32768;
  int data[arraySize];
  for (unsigned c = 0; c < arraySize; ++c)
      data[c] = std::rand() % 256;
    // !!! With this, the next loop runs faster.
    //std::sort(data, data + arraySize);
  clock_t start = clock();
  long long sum = 0;
  for (unsigned i = 0; i < 100000; ++i) {
      for (unsigned c = 0; c < arraySize; ++c) { // Primary loop
          int t = (data[c] - 128) >> 31;//delete if
          sum += ~t & data[c];
      }
  }
  double elapsedTime = static_cast<double>(clock()-start)/CLOCKS_PER_SEC;
  std::cout << elapsedTime << '\n';
  std::cout << "sum = " << sum << '\n';
}

使用其他方式替换if分支语句,如果不能去掉,可以保证分支的顺序,大概率发生的选项可以放在前面。

if (mostLikely) {
  // Do something
} else if (lessLikely) {
  // Do something
} else if (leastLikely) {
  // Do something
}

868df39c8d7b161283a76d579c561038.png

1.SylixOS,这款嵌入式RTOS值得学习!

2.芯片短缺或将结束?数据来说话。。。

3.代码调着调着就失联了,你碰到过吗?

4.图文并茂详解STM32时钟配置

5.电力物联网与嵌入式系统,关系几何?

6.RT-Thread携手北航出版社共办人工智能师资培训

6b734af97f86b6964ff1720cde380bdd.gif

免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。

在C语言嵌入式开发中,使用过多的`if...elseif...else if`(也称为嵌套if语句或多重条件判断)可能导致运行不稳定的原因有: 1. **效率降低**:过多的条件检查增加程序的执行时间和空间开销,尤其是在嵌入式系统中资源有限的情况下,频繁的分支切换可能影响性能。 2. **控制流复杂性**:过多的嵌套使代码变得难以理解和维护。当条件判断过于复杂时,可能引入隐藏的逻辑错误,导致程序行为难以预测。 3. **堆栈溢出**:如果嵌套的if-else语句很多,并且每个条件分支内部都有递归调用或其他深度嵌套结构,可能耗尽系统栈空间,特别是对于嵌套层级较高的情况。 4. **实时性问题**:在实时系统中,复杂的条件判断可能导致错过关键的时间点,影响任务的响应时间,从而影响系统的稳定性。 5. **编译器优化**:现代编译器虽然能进行一些优化,但面对大量嵌套的if-else,它们可能无法完全消除所有的条件跳转,这还是可能导致性能瓶颈。 为了解决这个问题,你可以考虑以下策略: - **重构代码**:尝试合并相似的条件,减少嵌套层次。 - **使用switch语句**:有时switch结构比多个if更高效,尤其是当有固定数量的选项时。 - **使用状态机**:如果条件逻辑可以转换为状态管理,可以提高代码的可读性和效率。 - **使用预处理器宏**:在编译时展开条件,但注意不要过度依赖宏,这可能导致难以调试的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值