分支预测
在cpu运行的过程中,当遇到条件判断的指令时,需要等这条指令的结果计算出来后,才能确定下一条该从指令寄存器中取出的指令是什么,这意味着在cpu流水线中,条件判断指令在进行译码和执行时,cpu的指令寄存器和指令译码器是处于等待状态的(一般情况是A指令译码时下一条指令同时被取出来;A指令执行时,下一条指令同时被译码)
分支预测就是在条件判断指令的计算结果出来前,猜测下一条指令是什么,从而避免指令寄存器和指令译码器处于等待状态,进而提高cpu性能
分支预测有多种策略,比如按顺序执行、按上一次的状态预测等
应用
有这样两个程序
int main()
{
int i=0, j=0, k=0, tmp=0;
for (i; i<10; i++){
for (j; j<10; j++){
for (k; k<1000000000; k++){
tmp++;
}
}
}
}
int main()
{
int i=0, j=0, k=0, tmp=0;
for (i; i<1000000000; i++){
for (j; j<10; j++){
for (k; k<10; k++){
tmp++;
}
}
}
}
这两个程序的执行次数一样,但如果分支预测策略为按顺序执行的话,那第一个程序就要快于第二个程序,可以看看汇编代码,执行gcc -g -c t.c; objdump -S t.o
int main()
{
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
int i=0, j=0, k=0, tmp=0;
4: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
b: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
12: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp)
19: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%rbp)
for (i; i<10; i++){
20: eb 23 jmp 45 <main+0x45>
for (j; j<10; j++){
22: eb 17 jmp 3b <main+0x3b>
for (k; k<1000000000; k++){tmp++;}
24: eb 08 jmp 2e <main+0x2e>
26: 83 45 f0 01 addl $0x1,-0x10(%rbp)
2a: 83 45 f4 01 addl $0x1,-0xc(%rbp)
2e: 81 7d f4 ff c9 9a 3b cmpl $0x3b9ac9ff,-0xc(%rbp)
35: 7e ef jle 26 <main+0x26>
for (j; j<10; j++){
37: 83 45 f8 01 addl $0x1,-0x8(%rbp)
3b: 83 7d f8 09 cmpl $0x9,-0x8(%rbp)
3f: 7e e3 jle 24 <main+0x24>
for (i; i<10; i++){
41: 83 45 fc 01 addl $0x1,-0x4(%rbp)
45: 83 7d fc 09 cmpl $0x9,-0x4(%rbp)
49: 7e d7 jle 22 <main+0x22>
}
}
}
4b: 5d pop %rbp
4c: c3 retq
可以看到在整个k循环中,cpu分支预测只会错误一次,就是在要退出k循环的时候,cpu会预测执行第35行代码,而实际会执行第37行。单看j循环和i循环也是错误一次分支预测,但j循环包含j次k循环,i循环包含i次j循环,所以总的分支预测错误次数为i * j,第一个程序i * j为100, 第二个程序i * j为10000000000, 所以第一个程序速度更快