分支预测的英文名字是「Branch Prediction」,分支预测如何工作的,为什么影响执行效率?
分支预测对程序的影响
分析如下代码:
#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数组排序后执行的时间上发生了非常大的变化。
所以,他们发生了什么事情呢?
导致结果不同的原因就是分支预测,分支预测是CPU处理器对程序的一种预测,和CPU架构有关系,现在的很多处理器都有分支预测的功能。
CPU在执行这段代码的时候
if (data[c] >= 128) sum += data[c];
CPU会有一个提前预测机制,比如前面的执行结果都是true,那么下一次在判断if的时候,就会假设是true来处理,让下面的几条指令提前进入预装。当然,这个判断不会影响实际的结果,只是为了让CPU并行执行代码。
CPU执行一条指令分为四个阶段:
分阶段执行,也就是常说的pipeline(流水线执行)。先给出一个预测结果,让流水线直接执行,执行对了则继续;执行错了,则退回去重新执行,直到对为止。
例如一段代码如下:
int a = 0;
a += 1;
a += 2;
a += 3;
CPU并不是运行完a = 0结束后,才开始执行a+=1,实际CPU是在运行a=0的第一条读执行后,马上就去运行a+=1的读执行了。这样按流水线执行,速度上得到了大幅度的提升。
但是,对于if 语句,存在分支预测的情况
通过比较我们可以发现,如果存在分支预测的时候,就让执行速度变快了。如果预测失败,也会影响执行的时间。
在前面的例子中,没有对数组排序的情况下,分支预测大概率会失败,这会导致指令执行结束后重新取下一条指令执行,无法发挥流水线的效果,严重影响执行效率。
而排序后的例子中,分支预测一直处于成功的状态,CPU的执行速率得到大幅度地提升。
如何解决分支预测引起的性能下降
分支预测存在一定的能性下降,想让性能提升的方法就是尽量少用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
}
免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。