在写代码的时候,有时会遇到在循环遍历一个数组时,如果代码块里有做if判断的操作时,我们有个优化的经验,就是先将数组先排序,然后再进行遍历比较操作时,效率会有比较大的提升。
一个简单的测试:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Test3 {
public static final void main( String argc[] ) throws Exception {
//生成十万个随机数
List<Integer> randomNumbers = new ArrayList<Integer>();
Random rnd = new Random();
for(int i=0; i <= 100000; i++){
randomNumbers.add(rnd.nextInt(10));
}
//未将randomNumbers排序,将randomNumbers大于五万的数放入results1
List<Integer> results1 = new ArrayList<Integer>();
long start = System.nanoTime();
for(int i=0; i <= 100000; i++){
Integer a = randomNumbers.get(i);
if(a > 50000){
results1.add(a);
}
}
long end = System.nanoTime();
System.out.println(end - start);
//将randomNumbers排序,将randomNumbers大于五万的数放入results1
Collections.sort(randomNumbers);
List<Integer> results2 = new ArrayList<Integer>();
long start1 = System.nanoTime();
for(int i=0; i <= 100000; i++){
Integer a = randomNumbers.get(i);
if(a > 50000){
results2.add(a);
}
}
long end1 = System.nanoTime();
System.out.println(end1 - start1);
}
}
运行结果:
1138865
762985
那为什么排好序的代码在判断的时候效率会更高呢?原因在于本文的主题:分支预测。
简单来说,就是在做if语句判断时,结果要么是A,要么是B,如果在判断前有个预判,执行A或者B,可以预先做些操作,然后通过判断来确认判断的正确性,如果预判的准确率较高,那么效率提升地就高,而预判一般地基础是之前做地判断结果,所以排序后地判断,能保证这个预判地连续性和正确性,提升了效率。
具体来说,这其实是个CPU level的操作,一般的,指令流水线中的第一条指令已进入到译码阶段,而第二条指令已进入到提取阶段(准备进入译码器),如果发现第一条指令是分支指令(如跳转到某个地址),则指令预取队列中下一条及下下条等指令预取无效。这时(确切地说,等到第一条指令执行期间形成了分支的目标地址),需从目标地址中现取指令,并交付执行,同时应立即清除指令预取队列,再将目标地址后面的指令预取过来填到队列中。这表明,一遇到分支指令,整个指令流水线就被打乱一次,稍后才能恢复到正常。显然,这影响了机器的运行速度。为此,在Pentium处理器中使用了分支目标缓冲器(Branch Target Buffer,BTB)来预测分支指令。
BTB实际是一个能存若干(通常为256或512)条目的地址存储部件。当一条分支指令导致程序分支时,BTB就记下这条指令的目标地址,并用这条信息预测这一指令再次引起分支时的路径,预先从该处预取。下面看一下BTB在循环程序中应用。循环程序在程序设计中使用得十分普遍。在指令级目标程序中构成循环程序需要用转移指令(条件转移指令或无条件转移指令)。看下例:
MOV CX.100
LOOP: ……
……
DEC CX
JNZ LOOP
……
在第一次执行到JNZ指令时,预测的转移地址是存在BTB中的前面一条JNZ指令的目标地址,不是LOOP,这一次预测是错误的。但执行后目标地址 LOOP便存入到BTB中。等到下一次执行到JNZ指令,就按BTB中的内容来预测,转移到LOOP,这是正确的。如此,一直到cx的值变为0之前,也都是对的。当再循环一次CX的值变为0时,JNZ指令因条件不成立而不实行转移,而预测仍是LOOP,预取仍按该预测进行,这是第二次预取错误。可见,该例中100次循环,有98次预测,确切地说,有98次预测指导下的预取是正确的。同理,对于1000次循环,就会有998次的预取是正确的。即循环次数越多,BTB带来的效益就越高。
参考:http://blog.sina.com.cn/s/blog_6c673e570100zfmo.html