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月份 ...
随机推荐
- BZOJ BLO 1123 (割点)【双连通】
<题目链接> 以下内容转自李煜东的<算法竞赛进阶指南> 题目大意:现在给定一张连通的无向图,不包含重边.现在输出$n$个整数,表示将第$i$个节点的所有与其它节点相关联的边去掉 ...
- 笔记-JS高级程序设计-BOM篇
BOM提供了很多对象,用于访问浏览器的功能.这些功能与任何网页无关. 1BOM的核心对象是window,它代表浏览器的一个实例,它是通过JS访问浏览器窗口的一个借口,同时又是ECMAScript规定的 ...
- 数据结构之二叉搜索树、AVL自平衡树
前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神 ...
- python与mysql交互中的各种坑
开始学python 交互MySQLdb,踩了很多坑 第一个 %d format: a number is required, not str 参照以下博客: https://blog.csdn.net ...
- dns服务扩展
- SpringBoot的第一个web项目
这一节主要是讲springboot搭建简单的web项目. 首先pom文件新增spring-boot-starter-web依赖,pom文件如下所示 <?xml version="1.0 ...
- VMware5.5-虚拟机的迁移和资源分配
虚拟机的迁移 迁移:将虚拟机从一台主机(或数据存储)移到另一台主机(或数据存储). 迁移类型: 冷迁移 迁移处于关闭状态的虚拟机. 挂起 迁移处于挂起状态的虚拟机. vMotion 迁移处于开启状态的 ...
- Codeforces.612E.Square Root of Permutation(构造)
题目链接 \(Description\) 给定一个\(n\)的排列\(p_i\),求一个排列\(q_i\),使得对于任意\(1\leq i\leq n\),\(q_{q_i}=p_i\).无解输出\( ...
- 英语口语练习系列-C29-鸟类-辨别身份-断弦的琴
鸟类 sparrow sparrow 英 ['spærəʊ] 美 ['spæro] n. 麻雀:矮小的人 swallow swallow 英 ['swɒləʊ] 美 ['swɑlo] vt. 忍受:吞 ...
- 英语口语练习系列-C28-海滨-辨别身份-悬崖边的树
beach 海滨 brown hut near body of water green trees near sea during daytime photography 海滨度假胜地 beach [ ...