Effective Java 第三版——67. 明智谨慎地进行优化
Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

67. 明智谨慎地进行优化
关于优化有三个格言,每个人都应该知道:
- 更多的计算上的过失是以效率的名义(不一定实现它)而不是任何其他单一原因——包括盲目做愚蠢的事情。
——William A. Wulf [Wulf72] - 我们应该不去计较小小的效率,大约97%时间里:过早的优化是所有问题的根源。
———Donald E. Knuth [Knuth74]
在优化方面,我们遵循两条规则:
- 规则1。不要优化。
- 规则2(只适用于专家)。先不要优化——也就是说,直到你有了一个完全清晰的还未优化的解决方案之前,不要优化。
所有这些格言都比Java编程语言的出现早二十年。 他们讲述了优化的深层真理:特别是如你过早优化的话,弊大于利。 在此过程中,可能会生成既不快,又不正确,且无法轻松修复的软件。
不要为了性能而牺牲合理的架构原则。努力编写好的程序,而不是快的程序。如果一个好的程序不够快,它的架构允许对其进行优化。好的程序体现了信息隐藏的原则:在可能的情况下,他们设计决策本地化为单个组件,因此可以在不影响系统其余部分的情况下更改单个决策(条目15)。
这并不意味着可以在程序完成之前忽略性能问题。 实现问题可以通过以后的优化来解决,但是如果不重写系统,就无法修复限制性能的普遍存在的架构缺陷。 事后改变设计的基本方面可能导致结构不良的系统难以维护和发展。 因此,必须在设计过程中考虑性能。
尽量避免限制性能的设计决策。设计中最难以更改的组件是那些指定组件之间以及与外部系统的交互的组件。这些设计组件中最主要的是API、线路层(wire-level)协议和持久化数据格式。这些设计组件不仅难以或不可能在事后更改,而且所有这些组件都可能对系统能够达到的性能造成重大限制。
考虑API设计决策的性能影响。 使公共类型可变可能需要大量不必要的防御性拷贝(条目 50)。 类似地,在一个公共类中应该使用复用更为合适,但依旧使用继承会把该类永远绑定到它的父类,这会人为地限制子类的性能(第18项)。最后一个例子是,在API中使用实现类型而不是接口会把你绑定到特定的实现,即使将来可能会编写更快的实现(条目 64)。
API设计对性能的影响是非常真实存在的。 考虑java.awt.Component类中的getSize方法。 这个性能关键方法决定,是返回Dimension实例,而且Dimension实例是可变的,强制此方法的任何实现都在每次调用时分配一个新的Dimension实例。 尽管在现代VM上分配小对象的成本很低,但是不必要地分配数百万个对象会对性能造成实际损害。
存在几种API设计替代方案。 理想情况下,Dimension应该是不可变的(条目 17); 或者,getSize可能已被两个返回Dimension对象的各个基本组件的方法所代替。 实际上,出于性能原因,在Java 2中将两个这样的方法添加到Component类中。 但是,预先存在的客户端代码仍然使用getSize方法,并且仍然会受到原始API设计决策的性能影响。
幸运的是,通常情况下,好的API设计与好的性能是一致的。为了获得良好的性能而包装API是一个非常糟糕的想法。导致包装API的性能问题可能在平台或其他底层软件的未来发型版本中消失,但是包装API和随之而来的支持问题将永远伴随着你。
一旦仔细设计了程序并生成了清晰,简洁且结构良好的实现,那么可能是时候考虑优化,假设你对程序的性能还不是不满意。
回想一下Jackson的两条优化规则是“不要优化”和“(只针对专家)还是先别优化”。他本可以再加上一条:在每次尝试优化之前和优化之后,要测量性能。你可能会对自己发现感到惊讶。通常,尝试的优化对性能没有可测量的影响;有时候,他们让事情变得更糟。主要原因是很难猜测程序将时间花在哪里。程序中你认为很慢的部分可能并没有错,在这种情况下,浪费时间来优化它。一般认为,程序将90%的时间花在10%的代码上。
性能分析工具可以帮助你决定将优化工作的重点放在哪里。这些工具提供了运行时信息,比如每个方法大约花费多少时间以及调用了多少次。除了关注调优工作之外,还可以提醒你需要进行算法更改。如果程序中潜藏着平方级(或更糟)算法,那么再多的调优也无法解决这个问题。必须用一个更有效的算法来代替这个算法。系统中的代码越多,使用分析工具就越重要。这就像大海捞针:大海捞针越大,金属探测器就越有用。另一个值得特别提及的工具是jmh,它不是一个分析工具,而是一个微基准测试框架,提供了非并行的可见对Java代码的详细性能 [JMH]。
与C和C++等更传统的语言相比,Java甚至更需要度量尝试优化的效果,因为Java的性能模型很弱:各种基本操作的相对成本没有得到很好的定义。程序员编写的内容和CPU执行的内容之间的“抽象鸿沟(abstraction gap)”更大,这使得可靠地预测优化的性能结果变得更加困难。有很关于性能的说法流传开来,但最终被证明是半真半假或彻头彻尾的谎言。
Java的性能模型不仅定义不清,而且在不同的实现之间、不同的发布之间、不同的处理器之间都有所不同。如果要在多个实现或多个硬件平台上运行程序,那么度量优化对每个平台的效果是很重要的。有时候,可能会被迫在不同实现或硬件平台上的性能之间进行权衡。
自本条目首次编写以来的近20年里,Java软件堆栈的每个组件都变得越来越复杂,从处理器到不同的虚拟机再到类库,Java运行的各种硬件都有了极大的增长。所有这些加在一起,使得Java程序的性能比2001年更难以预测,而对它进行度量的需求也相应增加。
总而言之,不要努力写出快速的程序——努力写出好的程序; 这样速度将随之而来。 但是在设计系统时要考虑性能,尤其是在设计API,线级协议和持久化数据格式时。 完成系统构建后,请测量其性能。 如果它足够快,你就完成了。 如果没有,请借助分析工具找到问题的根源,然后开始优化系统的相关部分。 第一步是检查算法选择:再多低级优化也不可以弥补差的算法选择。 根据需要重复此过程,在每次更改后测量性能,直到满意为止。
Effective Java 第三版——67. 明智谨慎地进行优化的更多相关文章
- Effective Java 第三版——83. 明智谨慎地使用延迟初始化
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——66. 明智谨慎地使用本地方法
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——45. 明智审慎地使用Stream
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版——52. 明智而审慎地使用重载
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——55. 明智而审慎地返回Optional
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版—— 86. 非常谨慎地实现SERIALIZABLE接口
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——53. 明智而审慎地使用可变参数
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- 《Effective Java 第三版》目录汇总
经过反复不断的拖延和坚持,所有条目已经翻译完成,供大家分享学习.时间有限,个别地方翻译得比较仓促,希望有疑虑的地方指出批评改正. 第一章简介 忽略 第二章 创建和销毁对象 1. 考虑使用静态工厂方法替 ...
- 《Effective Java 第三版》新条目介绍
版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...
随机推荐
- Alpha(5/10)
鐵鍋燉腯鱻 项目:小鱼记账 团队成员 项目燃尽图 冲刺情况描述 站立式会议照片 各成员情况 团队成员 学号 姓名 git地址 博客地址 031602240 许郁杨 (组长) https://githu ...
- 几个比较很重要的Shader相关教程
1. 论坛上有个兄弟写个的ToonShaderModel,可以参考ShaderModelhttps://github.com/EpicGames/UnrealEngine/pull/1552/file ...
- BZOJ.4897.[Thu Summer Camp2016]成绩单(区间DP)
BZOJ 显然是个区间DP.令\(f[l][r]\)表示全部消掉区间\([l,r]\)的最小花费. 因为是可以通过删掉若干子串来删子序列的,所以并不好直接转移.而花费只与最大最小值有关,所以再令\(g ...
- Android的系统属性:build.propSystemProperties
获取build.prop的键值信息: String sn = SystemProperties.get(SN_INFO); 其中key值为: public static final String SN ...
- ACPI状态简介
我们平时对电脑的的待机.休眠.睡眠等等都属于ACPI表示高级配置和电源管理接口(Advanced Configuration and Power Management Interface)范畴. AC ...
- CSS_级联和继承
2016-11-06 <CSS入门经典>第七章 1.在HTML中使用CSS样式表的三种方式: (1)内联的样式表. eg:<em style="background-whi ...
- js文档节点
一.创建节点: 1.创建元素节点:document.createElement("元素标签名"); 此方法可返回一个 Element 对象 <!DOCTYPE html> ...
- 多个string数组组装成一个List<Object>
最近遇到一个问题,数据库里面的数据存了一个多图字段和一个图片对应的排序,然后输出的时候需要按排序处理下. 当然,最容易想到的办法是遍历,然后添加,这次不想玩这么低级的代码,而且类似的需求项目中有好几个 ...
- Listener(1)—基础知识
一.监听器 1.概念: 专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监听的对象状态发生情况时,立即采取相应的 行动.Servlet规范为每种事件监听器都定义了相应的接口,w ...
- 8、jsのBOM对象与DOM对象
javascript的Bom和Dom对象使我们学习的重点,这篇随笔可以重点阅读 本篇导航: BOM对象 DOM对象 DOM Event(事件) 实例练习 一.BOM对象 1.window对象 所有浏览 ...