最近因为做架构重构,频繁大规模改代码,发现 Android 工程的构建速度简直到了无法忍受的地步,记得以前用 Intel 芯片的 Mac Book Pro 的时候,全量构建一次大概要 40 分钟,一番 deep dive 发现并不是工程有多挫,真正的原凶竟然是安全软件,顶配的 Mac Book Pro 跑出了 Mac Book Air 的感觉,后来有了 Apple M1 构建速度一下子提升了一个数量级,但最近体感明显的变慢了,我就很纳闷了,难道就我一个人觉得慢吗?

用户调研

之前也有遇到过 Gradle cache 导致构建时长成倍增长的问题,删了 cache 就正常了,但这次删了 cache 还是一如既往的慢,全量构建一次大概在 20 分钟左右,一天工作时间也就 8 小时,也就够全量构建个几次的,咱也不是好摸鱼的人呀,于是找了几个同学收集一下反馈,结果大家都觉得慢,但是也能忍受,究其原因居然是因为原来 40 分钟也熬过来了,现在 20 分钟比之前快了一倍呢!果然,没有对比就没有伤害,心态好不好完全取决于参考系。

初步调研

看来构建慢的问题并不是个例,但是得拿出有说明力的数据,我通过 git 的修改记录大概估算了一下,每个工程师每天花在构建上的时间大概在 1 小时左右,估算方法如下:

  • 总构建时长 = 全量构建时长 + 增量构建时长
  • 全量构建时长 = 全量构建次数 * 单次全量构建时长
  • 增量构建时长 = 增量构建次数 * 单次增量构建时长

这里面就有几个重要的数据:

  • 全量构建次数

    我们没有办法直接估算出全量构建的次数,但是,可以间接的推算出来,一般在以下情况下,会触发全量构建:

    • 每天的第一次构建:Gradle 的依赖解析策略缓存周期默认为 24 小时,所以,每天至少有 1 次全量构建
    • 改到了公共模块:这会导致几乎所有模块进行重新编译,通过 git log 就很容易算出出公共代码改动的频率,大概在 0.2 次/人天
  • 增量构建次数

    • 假设每次代码提交之前都得有一次编译,根据 git log 也能估算出增量编译的次数,大概在 10 次/人天

根据以上算法,结合自己的体感数据,便可以推断出:

  • 人均全量构建时长 = 1.2 次/天 * 20 分钟 = 24 分钟/天
  • 人均增量构建时长 = 10 次/天 * 3 分钟 = 30 分钟/天
  • 人均每天构建时长 = 54 分钟/天 😱😱😱

这么看起来问题还是挺严重的,于是,找了个同学来收集开发环境中的构建性能数据,收集了差不多半个月的数据,结果给出的结论是:

平均构建时间在 3.5 分钟左右,人均每天花在构建上的时长在 35 分钟左右,看起来问题不是很严重

What?! 😯😯😯 为什么跟我的推算有如此大的出入呢?

合理的 Metrics

根据前面通过 git log 拿到的基本数据,其实可以发现,增量构建的频繁远高于全量构建的频繁,如果取算术平均值,全量构建的极值就全被增量构建给平均掉了,那如何才能从数据中发现真正的问题呢?

去 TM 的平均值!

我们的关注点应该是 “每人每天在构建上花费的时长”,而不是“单次的构建时长”,那上面的人均每天 35 分钟有什么问题呢?这是所有人的数据的算术平均,因为“人和设备”之间的差异,导致每个人的情况有所不同,算术平均隐藏掉了这些差异,对于机器配置好的工程师来说,可能每天的构建确实不是问题,但其实每个人的机器配置是有很大的差别的,因为入职时间的先后问题,有的人还在用 Intel 芯片的 Mac Book Pro, 有的人已经换 M1 了,M1 也有不同的核数,有的 10 核,有的 12 核等等,如何从数据上发现这些差异呢?

直方图 (Histogram)

根据原始的构建性能数据,以 username 进行分组,按天求和,便可以得出每个工程师每天的构建时长,然后,取每个工程师每天的构建时长的 P90,以 30 分钟为一个桶,做直方图:

Histogram of Build Performance Per Person

从图中,我们可以看到,将近一半的工程师每天花在构建上的时间已经超过了 1 小时,有的甚至更久,达到了 4 小时,同时,我们也发现直方图最右边的两个点 x={20, 31} 偏离太远,其实是噪点,如何去噪点呢?

缩尾直方图 (Tail-Trimmed Histogram)

在统计学中,对于有“长尾”或者极值特征的数据,截尾 (Trimming)温莎化 (Winsorizing) 是比较常见的去噪手段。

之所以上面的直方图的尾部有两个噪点,根本原因是因为构建的时候,电脑被合上导致进程被挂起了,我们可以采用 截尾 (Trimming) 的方式来去掉图上的噪点:

Trimmed Histogram of Build Performance Per Person

缩尾之直方图到底是如何缩的呢?我这里用到的方法是——基于原始构建数据以分钟为单位做直方图:

Original Build Performance Histogram

然后,通过累积频率来找出 P99.8 的桶,并从其后截断:

Trimmed Build Performance Histogram

然后从原始的构建记录中去掉被截掉的构建记录(噪点数据)就得到了上图的缩尾直方图。

真正的问题

通过缩尾直方图,我们不难发现:

  • 14.29% 的工程师每天要花至少 2 小时在构建上
  • 42.86% 的工程师每天要花至少 1 小时在构建上

很显然,这一结论更符合我的体感,而不是“人均每天花在构建上的时长在 35 分钟左右”

Reference