本报道根据 2015 年 QCon 上海大会第二天早上 Gil Tene 的主题演讲内容整理而成。Gil Tene 是 Azul Systems 公司的联合创始人兼 CTO,在过去的 25 年一直从事虚拟机和运行时技术的开发,兴趣主要是研究系统响应和延迟行为。Gil 经常在全球范围的技术大会上发表演讲,他还是 JavaOne 明星讲师。他作为主要作者提出了 C4(Continuously Concurrent Compacting Collector)垃圾收集算法,该算法也是 Azul 的响应式 Java 平台的基础。他擅长 Java 虚拟化、弹性内存、各种托管运行时等技术,是著名的 Java 和 JVM 专家,过去也从事过操作系统、网络交换机、防火墙等系统的设计与构建。本次 QCon 上他带来的演讲主题为《 Pragmatic Performance 》,分享他如何以实用主义的眼光去看待“性能”这件事。
今天的分享并不是要告诉你们有关数据如何测量、有关如何调优的技巧。我在多年从业过程中犯下了非常多的错误,并且有幸从其中的一些错误中学到了新的东西。这些是我今天想要跟大家分享的,我希望大家能够反思自己对“性能”这个东西的理解,反思我们面对“问题”时的思考方式。
“实用”一词,是相对“理论”而言的。要解释“实用”和“理论”之间的区别,最简单的例子就是看屏幕上的这辆跑车。如果你问这样的问题:
“这辆车能跑多快?”
那么你在问的就是它的“理论速度”。你得到的答案可能是 189 迈 / 秒,这个速度完全取决于汽车发动机的极限转速、汽车的外型设计等自身因素。
如果你再问另一个问题:
“这辆跑车和一辆电动自行车相比,哪个更快?”
这也是一个有关“理论速度”的问题。而从“实用”的角度应该提出的问题是:
“在周五上午 9 点的北京四环路上,这辆跑车和一辆电动自行车相比,哪个更快?”
或者是:
“在不出任何交通事故的前提下,这辆跑车能在周五晚上 9 点的北京四环路上跑多快?”
今天我们做软件的,也经常会问这样类似的问题。比如:
“这个系统 / 这个工具能够达到每秒多少次查询?”
这就是个太过于简化的“理论问题”。这一个理论问题的背后,隐藏着无数个可能的“实用”问题,它们各自有各自的边界,或者前提条件。在进入这个问题本身之前,我们务必需要仔细思考有关“这个问题本身”的一些重要问题,比如:我是否需要针对每一个具体问题进行回答并提供正确的答案?是不是可以只回答简单的那些问题?是否一定要在下一个星期就提供成万上亿个答案?
在比较系统的性能时,要避免把苹果和橘子放在一起比较。比如,系统 A 的理论性能是每秒处理 X 个请求,但无法满足部分场景的需求;系统 B 的理论性能是每秒处理 0.9X 个请求,并且能够满足所有场景的需求。这时我们不能说,“系统 B 更慢,但更可靠”。这种比较是没有意义的。真正有意义的比较是获取系统 A 在满足所有需求的情况下的真实性能,并将其与 B 在同等条件下达成的性能进行比较。
我们说到系统的性能,经常使用下面这样的一些指标:
- 每秒可以进行多少次执行
- 延时,或者是响应时间
- 宕机率
- 系统宕机之后到恢复到正常状态所用的恢复时间
此类的指标可以有非常多,而且会互相影响。比如在一个场景下你需要系统能够在宕机后 1 小时内恢复,后来需求改变到需要在 1 分钟内恢复,我们如果要评估修改之后的系统,则不能仅仅看它的恢复时间是不是达到 1 分钟了,还得看其他指标——每秒执行次数、延时、宕机率——是不是仍然维持在之前的状态。
下面举例说明。比如我们对系统有如下要求:无论在多么忙碌的场景下,都要 99.9% 的请求在 20 毫秒内返回。那么,如果该系统处理 1 次请求需耗时 100 微秒——相当于每秒 1000 次处理的能力,这样的系统是否能够满足我们的要求?
答案仅仅是“有这个可能”。想象一下,如果 1 毫秒内进入了 50 次请求,会造成怎样的结果?
上图是我们一个线上系统的真实数据。横轴是时间,跨度为 1 天,刻度为 1 秒;纵轴代表“这一秒的第一个毫秒内进入的请求数”。蓝色的部分是记录的数值,红色的横线是根据系统的处理能力画出的理论最大处理能力。可以看到在不少时间下,蓝色的峰值都超过了红线。
我们然后当然是用“排队”的方式来处理这些峰值请求。但是当我们用到队列的时候,一定要搞清楚一个问题,那就是我们测量的“响应时间”是不是包含了那些排队的时间。经常发生的情况是,我们测量的可能是“服务处理时间”,而并非真正的“请求响应时间”。就好像你去星巴克买咖啡,服务生平均制作一杯咖啡的时间就是“服务处理时间”,而“请求响应时间”则是从你开始站在等待买咖啡的队伍末尾开始计算的。实际上我们在请求数频繁大于处理能力的高峰期时去看真实的延时数据,往往是这样的:
我们总是太快的开始测量。我们一开始的确能拿到一些测量数据,但到了生产环境下,我们往往会为系统的实际表现大吃一惊。
所以在“测量性能”之前我们应该做些什么呢?一个是要明确需求。一切需要进行性能测试的系统至少应该是正确的,即,功能上正确,符合用户对可用性的要求,等等。如果系统本身有错误,那么性能测试的结果无论如何都是无效的。另一个就是要认识到环境的限制、环境的边界。
接下来我想谈论的话题跟垃圾回收(GC)有关。一种通用的说法是 GC 的使用会影响程序的性能,所有需要性能的地方还是推荐用 C/C++/Objective-C。但我今天想请大家从“实用主义”的角度思考性能这个问题。
看看这三个交通工具。宇宙飞船和电动自行车,哪个速度更快?按照理论速度进行比较,答案毋庸置疑。
但我再问:哪一个交通工具能够让我在 15 分钟内从我家移动到我的上班地点?
或者:就我需要的速度而言,哪一个交通工具是我现在能用上,或者最晚在 6 个月之内能够用上的?
说 GC 影响程序的性能,其实主要还是考虑到“浪费资源”这一层。而这种思路其实要追溯到计算机发展阶段的早期。我也经历过那个时代,那是 30 年前,256KB 的存储被我看作是宝贝一样。如果让那时候的我看到今天的 iPhone,我恐怕会把它当作宝山一样看待,但更大的感觉可能是“气愤”。为什么?这样一台小巧的、多核的、具有大量存储空间、有如此分辨率和色彩的屏幕的大宝贝,是被用来做什么呢?打电话。听音乐。开玩笑吧,你们用这样珍贵的资源来做“听音乐”这种无聊的事情?
这是两种完全不同的观念。30 年前,计算资源被视为珍贵,因此以“节省使用”为正道。而今天,计算资源被视为白菜,因此以“浪费使用”为正道。事实上,正因为我们把计算资源看成白菜,硬件才能有今天这样快速的发展。
所以就今天的程序而言,什么才是“更高的性能”?那并不是最节省使用计算资源的软件,而是最浪费计算资源、却最节省时间资源的软件。所以,尽情的浪费那些计算资源吧!
评论