我正在我的计算机体系结构课上做一项作业,我们必须在C++中实现分支预测算法(对于 Alpha 21264 微处理器架构)。
提供了一个解决方案作为示例。 此解决方案是全局份额预测器的实现。
我只是想了解给定的解决方案,特别是正在发生的事情:
*predict (branch_info &b) {...}
具体说来
if (b.br_flags & BR_CONDITIONAL) {...}
谁能给我一个解释?谢谢。
我认为Scott McFarling的以下论文提供了详细的答案:
http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-TN-36.pdf
让我用你的代码来解释。
unsigned char tab[1<<TABLE_BITS];
是模式历史记录表。选项卡中的每个条目都保留一个 2 位饱和计数器。条件分支的方向最终由计数器的 MSB 确定:
u.direction_prediction (tab[u.index] >> 1);
我们使用两个或更多位的计数器而不是一个位的原因是使模式不那么敏感,以减少误猜。例如
for( int i = 0; i < m; i++ )
{
for( int j = 0; j < n; j++ )
{
...
}
}
当下一次执行内部循环时,一位计数器将错误地预测分支,而 2 位计数器可以阻止它。
接下来是如何在模式历史记录表中找到正确的模式。
天真的方法是使用分支地址作为索引。但它忽略了不同分支之间的相关性。这就是引入全球分支历史的原因(有关更多详细信息,请参阅 http://www.eecg.utoronto.ca/~moshovos/ACA06/readings/two-level-bpred.pdf)。
在您的代码中,
unsigned int history;
是存储全局分支历史记录的分支历史记录寄存器。
然后有些人发现,将全球分支历史记录和分支地址组合为索引可以比仅使用其中之一更准确的预测。原因是全局分支历史记录和分支地址都会影响分支模式。如果忽略其中之一,则不同的分支模式可能会散列到模式历史记录表的同一位置,从而导致冲突问题。
在提出Gshare之前,有一个名为Gselect的解决方案,它使用全局分支历史和分支地址的串联作为模式历史表的索引。
Gshare提供的解决方案是哈希函数
index = branch_addr XOR branch_history
这就是以下代码的确切含义:
u.index = (history << (TABLE_BITS - HISTORY_LENGTH)) ^ (b.address & ((1<<TABLE_BITS)-1));
Scott McFarling的论文提供了一个很好的例子来展示Gshare如何比Gselect更好地工作:
- 分行地址=1111_
- 1111 全球分行历史记录=0000_0000 分行地址=1111_
- 1111 全球分行历史记录=1000_0000
假设我们使用以下 Gselect 策略来防止偏见:
index = { {branch_addr[7:4]}, {branch_history[3:0]} }
然后 Gselect 将为这两种情况生成 1111_0000,而 Gshare 可以区分不同的模式。
据我所知,Gshare是迄今为止消除碰撞的最佳解决方案。