CPU分支预测(Branch Prediction)详解
基本概念
分支预测是现代CPU中的一种重要优化机制,用于预测条件分支指令(如if-else语句)的执行路径。CPU会根据历史执行记录来预测下一次分支的走向,从而提前执行预测路径上的指令。
工作原理
- 流水线执行:CPU采用指令流水线技术提高效率
- 预测执行:在条件判断结果出来之前就开始执行预测路径
- 投机执行:预测正确则继续,错误则回滚并执行正确路径
- 分支目标缓冲器(BTB):存储分支历史信息
预测策略
-
静态预测
- 向前分支默认不跳转
- 向后分支默认跳转(适用于循环)
-
动态预测
- 一位预测器:记录上次是否跳转
- 两位预测器:需要连续两次预测错误才改变预测
- 相关预测:考虑多个分支之间的关系
测试分支预测
既然CPU在IF语句执行前就将对应指令放入流水线执行了,即使可能因为预测失败而被冲洗掉,仍然可以从缓存中查看CPU的痕迹。
预测代码
// 使用GCC编译: gcc -O0 predict.c
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <x86intrin.h>
#define PAGE_NUM 256
#define CACHE_HIT_THRESHOLD 100
// 获取访问时间
static inline int get_access_time(uint8_t *page) {
unsigned long long tick1, tick2;
unsigned int aux;
tick1 = __rdtscp(&aux);
tick2 = __rdtscp(&aux);
return (int)(tick2 - tick1);
}
static uint8_t mem_pages[PAGE_NUM][1024 * 4] __attribute__((aligned(4096)));
// 检测缓存中的页面
static int detect_cached_page() {
for (int i = 0; i < PAGE_NUM; i++) {
// 混合访问顺序以防止步长预测
uint8_t mix_i = ((i * 167) + 13) & 255;
if (get_access_time(mem_pages[mix_i]) < CACHE_HIT_THRESHOLD) {
return mix_i;
}
}
return -1;
}
int data = 100;
int main() {
// 初始化内存页
memset(mem_pages, 1, sizeof(mem_pages));
// 测试循环
for (int i = 0; i < 10; i++) {
// 刷新缓存
_mm_clflush(&data);
for (int j = 0; j < PAGE_NUM; j++) {
_mm_clflush(mem_pages[j]);
}
// 分支预测测试
if (data == 0) {
data = mem_pages[33][0];
}
// 检测缓存页面
int cached_page = detect_cached_page();
printf("cached page: %d, data: %d\n", cached_page, data);
}
return 0;
}
- cpp的likely/unlikely就是用来优化分支预测的
动态预测
- tage算法