性能优化的边界-不该优化什么
上周写过一篇 性能优化的边界,我应该给它加个副标题 “什么时候该适可而止”,因为我今天要写另一个性能优化的边界,“不该优化什么”。
没有经过测量的优化都是想当然的,几乎无效的优化,常见于初入行的,但没什么悟性的资深行业老人也如此。我仍以自己为例,掘心自食。
当自己的代码部署上线性能不符合预期,我不会觉得哪里出了问题,反而觉得 “优化一番” 就可以更好。我会依次去调内核参数,优化系统调度器,优化 TCP 协议栈实现,优化 C 库函数,终于到了我自己的代码时,我会聚焦在 “某个库函数包含多少指令” 上,试图减少函数调用。
我曾经把好多 TCP 拥塞控制算法模块修改预定义宏后重新编译,甚至把它们抽做参数后调参,优化 BBR,CUBIC,MPTCP,我甚至优化 memcpy,但最终这些都弄巧成拙,意味着这些都没有问题,问题在于我使用它们的姿势不对,我对它们的排列组合方式错了。
大开合的性能提升往往来自于粗粒度的排列组合,而不是细粒度的优化,也就是说,知道哪种场景选择哪个现成的构件去搭建系统,比优化单独构件的收益边际更高,随着边际收益递减,才需要过渡到更细粒度的优化,例如优化算法,但往往在这个时候,系统表现已经足够好了,且有能力继续优化的工人也非常稀少了。
这段话意思是,绝大多数肉眼可见的性能提升来自于对既有技术构件的选型,而对细节的细粒度优化收益小到毛毛雨,而成本往往也支付给了根本没有能力做此类优化的经理和工人,换句话说,这类优化除了竞速,内卷样式的烧钱之外,并没有实际意义。
越精细的工作,对从业者要求越高,并不是普通人能做的,且边际收益很低的性能优化工作,在行业早期蓬勃发展时,却有相当一批人在做,待到行业瓶颈期甚至平台期,首先被裁撤的也是这类工作岗位。
举几个例子:
- 用 DPDK 重构 Netfilter-NAT 后性能提升立竿见影,不要再去优化 DPDK-NAT 本身;
- 使用 BBR 替代 CUBIC 后吞吐明显增加,不要再去优化 BBR 算法;
- 直接使用系统的 MPTCP Sched,不要试图自己想当然写一个更好的;
- 不要试图移植顶会论文里的思想到真实的现网环境,大概率血本无归;
- 不要怀疑公共构件,比如内核调度器,协议栈的实现问题造成了巨大性能损耗;
- 公共构件可能不是最优,有能力优化它们的人只有一小撮,大概率不包括你;
理由很简单,立竿见影的大开合收益对人的要求很低,而人又是如此之多,一个公共构件已经存在了这么多年,如果它有很大的性能问题,怎么可能轮到你去优化,而它那高精但收益很小的性能问题,你却又没有能力优化。即便被遗忘在角落的黑科技,也需要一定的悟性和沉淀积累去接住它们,但你大概率没有这些。
工人绝大多数时间是在用现有构件搭建新系统,而不是制造新的构件,因此知道在什么场景下适合选用什么构件更加重要,所谓选型,而制造新构件是工程师而不是工人的事。例如知道什么场景下 CUBIC 比 BBR 更有优势以及反过来如何,远比试图设计一个更优的拥塞控制算法更有意义,设计新拥塞控制算法是 VJ 他们的事。
虽然极致优化边际收益很低,有极致优化能力的人很少,但仍有大量普通工人投入到这个领域,这反过来表明大开合躺赚的事已经没了,没事做了,只能卷向无能为力的事,大家都无力继续优化,也就能多优化几年,这就是云计算 infra 领域普遍热衷于优化的原因。如今 AI 来了,势能有了新的口子,企业和社会必然会撤掉此前云 infra 的资源转投向 AI,搞优化的被降本增效,搞 AI 的收入暴涨。
注意,“内卷“” 的反义词是 “开展”。
浙江温州皮鞋湿,下雨进水不会胖。