Branch Prediction – Lab4
代码开源地址:https://github.com/coder-shx/qrio_handout
文章目录
1)实验目的
通过扩展QRIO中的工具来练习有关分支预测的知识,基于程序中分支的采集/未采集结果,分析不同预测器的预测失误率。
2)实验设计
1. Static Predictor
void branch(bool taken, unsigned long pc) override {
/* TODO 1: static predictor */
/* bool _predict: */
/* true: predict the branch will always be taken */
/* false: predict the branch will always be not-taken */
// 总预测数加一
_total++;
// 如果预测错误,则未命中数加一
if(taken!=_predict) {_miss++;}
}
预测逻辑
- 构造时由
_predict
决定:true
则始终预测“跳转(taken)”,false
则始终预测“不跳转(not-taken)”。
统计更新
- 每遇到一次分支,
_total++
;如果实际结果taken
与预测_predict
不符,则_miss++
。
2. FlipFlopPredictor
void branch(bool taken, unsigned long pc) override {
/* TODO 2: flip-flop predictor */
/* char _hbt[N]: history branch table */
/* pc_bit: number of PC bits used to index the table */
// 更新总分支次数
_total++;
// 计算索引:pc/4 的低 _pc_bit 位
unsigned int index = (pc >> 2) & ((1 << _pc_bit) - 1);
// 读取历史分支表中的预测值
bool prediction = _hbt[index];
// 如果预测错误,更新未命中次数
if (prediction != taken) {
_miss++;
}
// 更新历史分支表中的值
_hbt[index] = taken;
}
索引计算
-
由于 PC 恒为 4 字节对齐,先做
pc >> 2
,再取其低_pc_bit
位:index = (pc >> 2) & ((1 << _pc_bit) - 1);
预测与更新
- 预测值即
_hbt[index]
(0 表示预测 NT,1 表示预测 T)。 - 比较后更新命中统计,并将该表项直接设置为本次
taken
。
3. CorrelatePredictor
void branch(bool taken, unsigned long pc) override {
/* TODO 3: correlating predictor predictor */
/* (m,n)*T correlating predictor means a table with T n-bit predictors, */
/* m bits used to represent the taken/not-taken of the last m branches. */
/* Here, n is 2 that we use 2-bit predictors. */
/* */
/* char _hbt[N]: history branch table */
/* int _history: records taken/not-taken sequences of previous branches */
/* int _pc_bit: number of PC bits used to index the table */
/* int _history_bit: number of history bits used to index the table */
_total++;
// 计算表大小:2^(pc_bit + history_bit)
unsigned int hist_mask = (1u << _history_bit) - 1;
unsigned int pc_mask = (1u << _pc_bit) - 1;
unsigned int hist_bits = _history & hist_mask;
unsigned int pc_bits = (pc >> 2) & pc_mask;
unsigned int idx = (hist_bits << _pc_bit) | pc_bits;
// 读取 2-bit 饱和计数器状态(0,1:预测 NT;2,3:预测 T)
unsigned char cnt = _hbt[idx];
bool prediction = (cnt >= 2);
if (prediction != taken) {
_miss++;
}
// 更新 2-bit 计数器
if (taken) {
if (cnt < 3) _hbt[idx] = cnt + 1;
} else {
if (cnt > 0) _hbt[idx] = cnt - 1;
}
// 更新全局历史:低 history_bit 位左移并插入当前结果
_history = (( (_history << 1) | (taken ? 1 : 0) ) & hist_mask);
}
联合索引
-
全局历史的低
_history_bit
位和(pc>>2)
的低_pc_bit
位拼接:idx = (history_bits << pc_bit) | pc_bits
2-bit 饱和计数器
- 状态值
0,1
→ 预测 not-taken;2,3
→ 预测 taken; - 更新时注意边界
[0,3]
饱和值。
历史寄存器滚动
-
每次插入最新分支结果,左移一位并截断:
history = ((history << 1) | (taken ? 1 : 0)) & hist_mask;
4. GSharePredictor
void branch(bool taken, unsigned long pc) override {
/* TODO 4: GSharePredictor */
/* index = pc-bit XOR history-bit */
_total++;
unsigned int mask = (1u << _pc_bit) - 1;
// 取 pc/4 的低 pc_bit 位
unsigned int pc_bits = (pc >> 2) & mask;
// 取历史的低 pc_bit 位
unsigned int hist_bits = _history & mask;
// XOR 得到最终索引
unsigned int idx = pc_bits ^ hist_bits;
// 读取、预测、更新与 Correlate 相同
unsigned char cnt = _hbt[idx];
bool prediction = (cnt >= 2);
if (prediction != taken) {
_miss++;
}
if (taken) {
if (cnt < 3) _hbt[idx] = cnt + 1;
} else {
if (cnt > 0) _hbt[idx] = cnt - 1;
}
// 更新历史:同上,记录最新一次分支结果
_history = (((_history << 1) | (taken ? 1 : 0)) & mask);
}
XOR 索引
-
将 PC 低位与全局历史低位做异或,得到分支表索引:
idx = ((pc>>2)&mask) ^ (history & mask)
饱和计数器 & 历史更新
- 与 CorrelatePredictor 相同的 2-bit 更新和历史寄存器截断逻辑。
3)实验结果
fib-arm64-static
bzip2
4)遇到的问题及解决方法
1. FlipFlopPredictor索引计算错误
-
问题:直接使用完整PC地址索引导致表空间浪费
unsigned int index = pc & ((1 << _pc_bit) - 1); // 错误:未考虑PC对齐
-
解决方法:优化索引计算,利用PC的4字节对齐特性
unsigned int index = (pc >> 2) & ((1 << _pc_bit) - 1); // 正确:取PC/4的低位
2. CorrelatePredictor历史更新溢出
-
问题:历史寄存器未做位宽限制导致索引越界
_history = (_history << 1) | (taken ? 1 : 0); // 错误:历史位无限增长
-
解决方法:添加位掩码限制历史寄存器长度
unsigned int hist_mask = (1u << _history_bit) - 1; _history = ((_history << 1) | (taken ? 1 : 0)) & hist_mask;
3. GShare索引混淆
-
问题:误将PC位与历史位独立拼接而非异或
unsigned int idx = pc_bits | hist_bits; // 错误:应为异或操作
-
解决方法:修正为异或计算
unsigned int idx = pc_bits ^ hist_bits; // 正确:GShare核心设计