网站首页 > java教程 正文
场景
事情是这样的,我先来还原一下场景,有如下图中的一段代码,这段代码的逻辑很简单。
- 先生成一个0-top范围的有序集合,比如top=100,那么就是生成[0,1,2,3,...99,100];
- 如果shuffle=true,则将这个集合顺序打乱,相当于洗牌;
- 然后遍历这个集合,统计出集合中数值小于top/2的数量,这个结果不管是否洗牌都是top的一半;
- 记录循环开始和结束的时间,看看循环总共花费多少时间。
按理说,不管这个集合有多大,都会执行top次的循环,每个循环里都要执行if判断,所以花费的时间不管是否洗牌应该都是一样的对吧。
但是,神奇的事情发生了!
public static void main(String[] args) {
perdication(1000000,true);
perdication(1000000,false);
}
我们执行这段代码,分别用有序的集合和打乱的集合执行,看看花费的时间。
从执行结果发现,顺序打乱的集合执行的时间比有序集合执行的时间竟然要长!!并且多次执行后,情况一样。
这是为什么呢?
原来计算机在背后帮我们做了一些事情,对我们的代码进行了一些性能上的优化。
计算机做的事情叫做分支预测。在开始说分支预测前,先要普及一个概念叫做指令管道。
指令管道
当我们编写任何计算机程序时,我们正在编写一组我们希望计算机按顺序执行的命令。
早期的计算机会将这些命令一次性执行。每个命令都被加载到内存中,全部执行,只有当它完成时才会加载下一个命令。
指令管道对这种情况的一种改进技术,它允许处理器将工作分成多个部分,然后并行执行。允许处理在执行一个命令时就将一下个命令加载进来准备就绪。
比如,我们有一个简单的程序:
int a = 0;
a += 1;
a += 2;
a += 3;
每一行代码都会由处理器进行加载(Fetch)、解码(Decode)、执行(Execute)、存储(Store)等步骤执行。
从上图可以看出这四个命令的执行是如何并行运行的,从花费的时间上变得更快。
会产生什么问题呢?
通过指令管道进行优化后,虽然能在多少情况下让程序运行变快,但是会导致出现一些问题。
有一些命令的执行,依赖于另一些命令执行的结果,而在该命令执行时可能依赖命令还没有执行完毕。
代码中的分支就可能导致这种问题发生。因为在分支部分执行方向只会选择其中的一个,并且在执行分支之前是不能知道走哪个方向的。
这会导致任何对分支执行方向的预测变得不安全,因为我们无法确定分支的具体走向。
int a = 0;
a += 1;
if (a < 10) {
a += 2;
}
a += 3;
这段代码从执行结果上和上面的代码一样,但是因为增加了一个if语句,会导致计算机无法预知if分支的走向,所以在执行if之前,不能加载后续的代码命令。
可以发现,因为引入了一个if分支,导致执行时间立马变长。
那这和我们最开始的分支预测又有什么关系呢?
什么是分支预测?
其实,分支预测是对指令管道优化的一个增强。计算机会尝试预测分支的走向,然后按照预测的结果进行一些指定的加载。
int a = 0;
a += 1;
if (a < 10) {
a += 2;
}
a += 3;
同样这段代码,计算机可能会预测a<10为true,这样一来,指令执行情况将变成下面这样:
可以看到,时间立马从原来的11变成9,提高了程序性能。
但是,这样预测是存在风险的,因为计算机也可能预测错误,如果执行结果和预测结果不一致,那么已经加载的命令不应该被执行,将会被丢掉之后重新加载新的命令。比如,我们将程序中的条件修改为a>10:
int a = 0;
a += 1;
if (a > 10) {
a += 2;
}
a += 3;
那么它的执行情况可能就是这样:
可以发现这比上面的执行时间要慢。
分支预测有什么影响?
现在我们知道了分支预测,那它对代码又有什么影响呢?
通常情况下,我们的业务逻辑并不复杂,或者说因为分支预测错误,导致多执行几个处理器的命令,对性能并不会有太大的影响。
但是在一些特定的场景下,这种机制可能就会带来巨大的性能提升。
再来回忆一下开始的代码。
就是因为在for循环中,对if条件的执行结果进行预测,并行的对指令进行加载,提高了执行效率。
而洗牌之后的集合,因为数字顺序被打乱,导致分支预测的结果多数情况下和实际不一致,导致需要丢掉命令后重新加载正确的命令执行。
小结
通过以上内容我们了解了分支预测是什么以及它对我们的程序产生的影响。可以让我们进行程序性能优化时,多一个选择。
如果你觉得本文有点意思,不妨点个赞吧。
猜你喜欢
- 2024-10-26 一些长时间GC停顿问题的排查及解决办法
- 2024-10-26 真的很实在,程序员如何精确评估开发时间?
- 2024-10-26 「JAVA面试」时间轮你了解吗?(java中的时间类)
- 2024-10-26 java判断当前日期时间大于指定日期时间 记录日常开发中的小搜索
- 2024-10-26 java如何优雅的实现时间控制(java 时间控件)
- 2024-10-26 整理 | Java日期工具类(java操作日期的类)
- 2024-10-26 Java8 新特性:新日期与时间(java 日期操作)
- 2024-10-26 java 和 go 的定时器对比(java定时器和多线程的区别)
- 2024-10-26 Java时间类Date与Calendar的区别与使用
- 2024-10-26 Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)