根据我最近对零刻(Beelink) GTi 11 mini PC 的评测, 大家可以了解到在配备 16 GB RAM 和快速 SSD 的 Intel Core i5 Jasper Lake 迷你 PC 上,想要构建Linux 内核大约需要 5 分钟(不包括模块)。在这个过程中,内核开发人员可能必须针对不同的目标和配置以及所有模块来进行构建,因此实际构建时间可能会增加。虽然可以通过投入更多硬件来加快内核的构建速度,但如果可以“ 直接通过软件优化来更快实现构建速度”也不失为一件好事。
这其实正是 linux 内核调度模块的负责人 Ingo Molnar 从 2020 年底以来一直在做的工作,他的“Fast Kernel Headers 项目”旨在消除 Linux 内核中存在的Dependency hell(相依性地狱)问题。他刚开始提出时,其目标是提高 20% 的速度,但一年多后的今天,结果令人惊讶万分。根据目标平台(x86-64、arm64 等)和配置,Linux 内核的构建速度提高了 50% 到 80%。

这其实是一项十分艰巨的工作,该项目由内部25个子树组成,有超过2200个代码需要提交,你们可通过以下方式获得:
1 |
git clone git://git.kernel.org/pub/scm/linux/kernel/git/mingo/tip.git |
Ingo Molnar 也详细地解释了为什么减少头文件依赖如此困难,并且需要这么多代码的提交。解释如下所示:
正如大多数内核开发人员所知,Linux 内核中的 include/ 和 arch/*/include/ 层次结构中大约有 10,000 个主要的 .h 头文件。在过去的 30 多年里,它们已经发展成为一组复杂的交叉依赖了,我们亲切地称之为“Dependency Hell”。
在 2020 年底我开始这个项目的时候,我就预测可能会有 50-100 个补丁。当时我也做了一些粗略的测量,发现通过减少头文件依赖的方式可以提高大约 20% 的构建速度,而且这样做不会对内核产生实质性的运行影响。这些似乎足以证明 50-100 次代码的提交是合理的。
随着补丁数量的增加,我看到了有限的性能提升。但是,到 2021 年年中的时候,我在这个分支中拥有了超过 500 次代码提交,因此我不得不放弃我的第二次尝试(!)。因为这两种方案根本无法扩展、不可维护,而且只能很勉强地提升 4% 的构建加速。因此使用500 个补丁来搅乱整个项目是不值得的,甚至都不值得告知公众。
在第三次尝试中,我引入了 per_task() 机制,它提供的灵活性大幅减少了依赖关系,这是一种提高可维护性的类型清理方法。通过这种方法,情况似乎也没有好很多,甚至在 1,000 次代码提交时,我的构建速度也还没有提高到 10%。此时我还是觉得这不是我可以向上游push代码甚至大肆宣布的事情。
但这次的尝试结果提供的数据确实非常清楚,也让我觉得 20% 性能的提升还是非常有可能的。所以我就一直在开发这个分支,在 2021 年秋季我提交了超过 1,500 次代码后,我发现大部分的加速都开始体现出来了。当加速超过 20% 甚至更多时,我其实非常惊讶。接着,在我的参考配置中就达到了现在的 78%提速。
其实,根据一旦依赖项的数量减少到最低限度,内核构建的消耗情况就有一个明显的非常线性的改进特性。
你们可能会觉得内核维护人员对接受大量补丁的态度都会十分谨慎态度,但其实Greg KH 的反馈很积极,尽管他也同时警告了潜在的维护问题:
这很“有趣”,但是你未来如何使 kernel/sched/per_task_area_struct_defs.h 和 struct task_struct_per_task 的定义如何保持同步呢?似乎你需要手动创建了它(这对于测试非常有用)。但从长远来看,通过“手动决定需要在此处做什么”的方式来保持所有内容都能被正确地使用,将会是一个很折磨人的事情。
撇开这个问题不谈,我又看了一眼这个分支。总的来说,它看起来似乎能更好地对之前复杂依赖关系进行清理。其中大部分可能会通过各种子系统树,在你们将它们拆分后,应该就能用于“主要的”.h文件的清理。不知道这是不是你们打算做的事情呢?
这个维护人员如何继续进行清理的讨论现在仍还在进行中。那么我们一起看一下其数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# # Performance counter stats for 'make -j96 vmlinux' (3 runs): # # (Elapsed time in seconds): # v5.16-rc7: 231.34 +- 0.60 secs, 15.5 builds/hour # [ vanilla baseline ] -fast-headers-v1: 129.97 +- 0.51 secs, 27.7 builds/hour # +78.0% improvement Or in terms of CPU time utilized: v5.16-rc7: 11,474,982.05 msec cpu-clock # 49.601 CPUs utilized -fast-headers-v1: 7,100,730.37 msec cpu-clock # 54.635 CPUs utilized # +61.6% improvement |
通过快速标头优化,Linux 5.16-rc7 的完整构建从 231 秒缩短到了 130 秒,或者说提升了大约 78%。你们可能觉得它只对总是从头构建内核的人有用,但其实增量构建从头文件清理中还是有挺多好处的:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
| v5.16-rc7 | -fast-headers-v1 |--------------------------------|--------------------------------------- 'touch include/linux/sched.h' | 230.30 secs | 15.6 builds/hour | 108.35 secs | 33.2 builds/hour | +112% 'touch include/linux/mm.h' | 216.57 secs | 16.6 builds/hour | 79.42 secs | 45.3 builds/hour | +173% 'touch include/linux/fs.h' | 223.58 secs | 16.1 builds/hour | 85.52 secs | 42.1 builds/hour | +161% 'touch include/linux/device.h' | 224.35 secs | 16.0 builds/hour | 97.09 secs | 37.1 builds/hour | +132% 'touch include/net/sock.h' | 105.85 secs | 34.0 builds/hour | 40.88 secs | 88.1 builds/hour | +159% |
构建速度提高了 173%。改进的主要原因是“关键内核标头的有效后预处理大小”“大幅”减少了,其中一些如下所列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
------------------------------------------------------------------------------------------ | Combined, preprocessed C code size of header, without line markers, | with comments stripped: ------------------------------.-----------------------------.----------------------------- | v5.16-rc7 | -fast-headers-v1 |-----------------------------|----------------------------- #include <linux/sched.h> | LOC: 13,292 | headers: 324 | LOC: 769 | headers: 64 #include <linux/wait.h> | LOC: 9,369 | headers: 235 | LOC: 483 | headers: 46 #include <linux/rcupdate.h> | LOC: 8,975 | headers: 224 | LOC: 1,385 | headers: 86 #include <linux/hrtimer.h> | LOC: 10,861 | headers: 265 | LOC: 229 | headers: 37 #include <linux/fs.h> | LOC: 22,497 | headers: 427 | LOC: 1,993 | headers: 120 #include <linux/cred.h> | LOC: 17,257 | headers: 368 | LOC: 4,830 | headers: 129 #include <linux/dcache.h> | LOC: 10,545 | headers: 253 | LOC: 858 | headers: 65 #include <linux/cgroup.h> | LOC: 33,518 | headers: 522 | LOC: 2,477 | headers: 111 #include <linux/module.h> | LOC: 16,948 | headers: 339 | LOC: 2,239 | headers: 122 |
上述的LOC是代码行数,你可以看到使用fast-headers-v1选项后LOC数值大大地缩小了。“headers”列也是如此,该列表示间接包含的头文件数量。支持的平台包括x86 32位和64位(启动测试和主机)、ARM64(启动测试)、MIPS 32位和64位,以及Sparc 32位和64位。不过,针对这些平台目前仅仅是已构建的阶段,暂时还未在实际硬件上进行测试。
其实,这些测试结果真的令人印象深刻。如果Fast Kernel Headers的代码提交被合并到上流代码中,它就可以延长现有构建场的生命周期,并能在一定程度上加快Linux开发的过程。
本文消息来源于ZDNet。

文章翻译者:Nicholas,技术支持工程师、瑞科慧联(RAK)高级工程师,深耕嵌入式开发技术、物联网行业多年,拥有丰富的行业经验和新颖独到的眼光!