2024年的第2个工作日,我在Ethan的读书群中接了一个新任务------读《深入理解软件性能》,过完年于2月底输出一篇读书笔记。
收到书当天,迫不及待翻过书中前言与目录,我心中出现畏难情绪:这本书很专业,我肯定不能在一个月内看完。时间转眼走完3个半月,终于在4月中旬,我将全书过完一遍。耗时如此之长,只因自己的执行力不强,很多个周末,本书都只安静待在书桌上。
如若执行力强些,一天阅读半小时,我该是能够在两个月内读完的。多出来的一个半月时间,让我心生惭愧,一种来自于不守信的惭愧,这期间,我请假一次后便不再敢主动给Ethan发信息,即便三次小任务打卡能换一本纸质书......
好在,我终于读完这样专业的一本书。本篇,便是读完本书后的读书笔记。
一、读完本书后收获的框架性认知
搬运书中内容作为本篇读书笔记主体内容之前,请允许我先用自己语言说一说我从书中收获到的认知。
我们写的程序,运行在计算机上面,有快有慢,绝大部分情况下,我们是希望这些程序能执行更快些的。如何让程序跑得更快些,可以从两个层面考虑:
一是较上的代码层面,整洁、可读性高、迭代性强的代码,往往会跑得更快。这更多来自于一种严谨态度,更整洁代码往往意味着更细心,考量更全面,选用更高效算法,许多重复计算会被提炼为一次计算,等等等等。
二是较底层的资源占用,也是本书重点关注的地方。决定程序运行速度快慢的瓶颈,无外乎CPU、内存、硬盘、网络和临界区五种基本资源,如果我们能够定位到某个时间点我们的程序都做哪些事,正访问哪些资源,那我们是会较容易找到性能瓶颈并打破这性能瓶颈的。
CPU瓶颈,可能是某些指令执行很慢,可能是一次执行指令太多,甚至可能是根本没有在执行;在《操作系统导论》中,提到SRAM、DRAM、硬盘等好几种存储设备,它们速度各异,其中有一个"缓存命中率"概念,考虑软件性能内存瓶颈时,缓存未命中会是一个主要考量点;硬盘,则需要考虑寻道时间、旋转时间和传送时间;网络的情况会很复杂,测量网速需要在两台机器上进行,此时需要考虑两台机器时钟一致性;最后是临界区和锁。
处理软件性能问题的大体流程是:观察现象,分析问题所在并找到性能瓶颈,最后是修复或者修改。大部分情况下,找到问题会比解决问题更难一些。定位瓶颈所在,并非是一件容易事,写log、代码中插勾子、各种性能分析软件的使用都会是技能点,一个可视化的仪表盘会很有用处。作者于书中分享了一个捕捉用户态执行和内核态执行的工具KUtrace。
二、那些可以运用的具体知识点
说完看完书本留在脑子中的框架性认知之后,本篇读书笔记接下来的内容,绝大部分来自于书中内容的搬运。这些内容,是阅读当时对我产生过触动的点,是那些我会在未来工作中可能用到的点。
(当然,不一样读者读同一本书,收获是会不一样的,收获依据读者的背景知识决定。我过往工作中,对性能优化部分负责的较少,于是触动点相应就会较少。我相信做过许多优化工作的读者,阅读本书时的触动点,将会超级多。)
1、本书的主要内容
本书分作四个部分。
第一部分介绍如何仔细测量4种基本的共享计算机资源------CPU、内存、硬盘(固态或机械)和网络------的性能。
第二部分介绍可用来观察计算机软件行为的现有工具和技术,日志、计数器、性能分析文件和跟踪工具。一些比较常用的现有工具是top、/proc和/sys伪文件、time、pref、oprofile、strace、ltrace、ftrace、mtrace、blktrace、tcpdump和Wireshark、locktrace等等。
第三部分作者构建了一个记录内核态执行和用户态执行每次切换的工具,这工具叫KUtrace,用户可以观察所有CPU核心上正在运行的所有程序和事务,查看所有的执行和未执行情况。
第四部分则是如何使用KUtrace,根据KUtrace获取的观察结果进行推理,然后找到具体的瓶颈点。
2、性能分析的步骤
对计算机底层知识的理解是性能分析的基础,会使用各种测量工具甚至自己能写工具是必要手段。
定位问题不能只靠猜测,还要通过测量、观察和推理知道真正发生了什么,基于此再做修复。
3、每一位优秀的程序员都应该知道的数字
以下内容来源于书中表格,还加入些我自己的理解。
- L1缓存引用,0.5纳秒(一秒等于1 000 000 000纳秒,十亿纳秒),从CSAPP第1.6节中得知,L0是寄存器,直接与CPU打交道,L1是最快的内存;
- 错误的预测分支,5纳秒,CPU采用分支预测使流水线中充满指令,如果选择错误,就需要回退,类似于我们步行时于岔路口的选择,选择错误便需要先回到原来路口;
- L2缓存引用,7纳秒;
- 互斥量锁/解锁,25纳秒;
- 主内存引用,100纳秒;
- 使用Zippy压缩1KB数据,3000纳秒(3微秒);
- 在1Gbit/s网络上发送2KB数据,20 000纳秒(20微秒);
- 从内存顺序读取1MB数据,250 000纳秒(250微秒);
- 在同一数据中心往返,500 000纳秒(500微秒);(此条说明我之前认为硬盘比网络会快些的看法是不准确的)
- 磁盘寻道,10 000 000(一万微秒,10毫秒,0.01秒);
- 从磁盘顺序读取1MB的数据,20 000 000(20毫秒,0.02秒);
- 从加州发送数据包到荷兰,再从荷兰发送回加州,150 000 000(150毫秒,0.15秒),距离有点远,速度有点快;
4、一些比较具体的优化细则
- 一些适度的循环展开对性能提升会有帮助,将多次执行但结果不变的计算识别出来搬运到循环外面。
- CPU与内存的交互,主要考虑的点是缓存命中率,将同一时间段内会用到的数据组织在一起是不错选择。
- 日志要少记一些,需要考虑时间和空间,文件存储的大小。日志可以使用位级记录,需要时再用一个程序回放。钩子要注意性能影响。日志时间要考虑时区问题。
- 要考虑不同机器上的时钟差异。(书中测量的两台机器间时间相差3.1ms。)
- 大块的数据总是会比零散数据更快,不管是内存、硬盘还是网络,对硬盘来说,小块数据意味着更多寻道,对网络来说,小块数据意味着更多的TCP连接建立过程。(当然不止TCP。)
- 观察是一件比较考研耐心的事。观察有一个设计原则:对于有时间约束的软件动态,一条好的经验法则是留出不超过1%的CPU和内存开销作为预算。应该先确定预算,再进行测量,并确认设计能够满足预算。
三、小结
《深入理解软件性能》,是一本很专业很难的书,作者更注重于从系统底层分析软件的性能瓶颈,阅读本书之前,是需要对计算机知识有一个比较全面了解的。
阅读本书时,我并未完成作者于书中布置的节末作业,所以本篇读书笔记,我很有些不自信。正如开篇所说,我确确实实收获了关于性能分析的框架性认知,但真正的实践,是还欠缺着的。不自信的另一来源是对书中许多细处的理解并不通透。我的完整过一遍,只在脑子中留下些许印记,这是一种不严谨态度。
我转头对自己说:"有了印记,待到用时再翻书。"
本书的推荐语为:"性能分析,是软件开发中必须要考虑的事情。如何考虑,怎样分析性能瓶颈,都可以在《深入理解软件性能》中找到具体的实施方法,测量、观察再推理,找到性能的瓶颈点,然后再修改。软件,会快起来的。"