性能文章>Linux 启动时间优化实战,2.41 秒启动应用!>

Linux 启动时间优化实战,2.41 秒启动应用!转载

3周前
174412

导言

快速启动嵌入式设备或电信设备,对于时间要求紧迫的应用程序是至关重要的,并且在改善用户体验方面也起着非常重要的作用。这个文章给予一些关于如何增强任意设备的启动时间的重要技巧。

 

正文

 

Hi, 我是老吴。

今天看了一个关于启动优化的讲座,简单总结一下。

本文的目标是尝试一些比较简单有效的方法,并不会覆盖所有的优化技巧。

目标系统

硬件:

Beagle Bone Black (Cortex A8)

USB 摄像头 + LCD

软件:

Linux 5.1 + Buildroot rootfs

FFmpeg,用于采集视频并解码到 LCD。


当前启动时间:

从上电到 LCD 显示第一帧图像:9.45 秒

 

1、优化编译器

ARM vs Thumb2

比较基于 ARM 或者 Thumb2 指令集编译出来的系统和应用。

ARM:rootfs 为 3.79 MB,ffmpeg 为 227 KB。

Thumb2:3.10 MB (-18 %),183 KB (-19 %)。

性能方面:Thumb2 的性能明显略有提升 (约小于 5 %)。

虽然性能有所提升,但是我个人还是会选择 ARM 指令集。

musl vs uClibc

Buildroot 里有 3 种 C库可以选择:glibc、musl、uClibc,这里我们只比较后面 2 种比较小巧的库。

musl:680 KB (统计 /lib 目录)。

uClibc:570 KB (-16 %)。

uClibc 节省了 110 KB,我们选择 uClibc。


2、优化应用程序

我们可以通过 ./configure 对 FFmpeg 的功能组件进行选择。

另外,还可以用 strace 和 perf 命令调试以优化 FFmpeg 的内部d代码。

优化后的结果:

文件系统:从 16.11 MB 缩小到 3.54 MB (-78 %)。

程序的加载和运行时间:缩短 150 ms。

整体启动时间:缩短 350 ms。

在空间的优化很大,但是在启动时间上的优化很小,这是因为 Linux 运行程序时只加载程序的必要部分。

 

3、优化 Init 和根文件系统

思路:

使用 bootchartd 分析系统启动并裁剪不必要的服务。

将 /etc/init.d/ 下的启动脚本合并为一个。

不挂载 /proc 和 /sys。

裁剪 BusyBox,文件系统越小,内核挂载可能会越快。

将 Init 程序替换成我们的应用程序。

静态编译应用程序。

裁剪掉不常用的文件,找出长时间不访问的文件:

$ find / -atime -1000 -type f


优化后的结果:

文件系统:裁剪 Busybox 后,从 3.54 MB 缩小到 2.33 MB (-34 %)。

启动时间:基本没改变,大概是因为文件系统本身就足够小了。

使用 initramfs 作为 rootfs:

一般情况下,Linux 系统会先挂载 initramfs,init ramfs 很小且位于内存中,再由 initramfs 负责负载根文件系统。

当我们将 Buildroot rootfs 裁剪得很小时,就可以考虑直接将其作为 initramfs 使用。

这样有什么好处呢?

initramfs 可以和 Kernel 拼接在一起,Bootloader 负责将 Kernel+initramfs 加载到内存中,内核不再需要访问磁盘。

内核不再需要 block/storage 和 filesystem 相关的功能,体积会变得更小,加载时间和初始化时间都会变小。

注意,需要关闭 initramfs 的压缩(CONFIG_INITRAMFS_COMPRESSION_NONE)。

优化后的结果:

即便禁用了 CONFIG_BLOCK 和 CONFIG_MMC 后,总启动时间仍多了 20ms。这可能是因为 Kernel + initramfs 拼在一起之后,内核变大了许多,而内核镜像是需要解压,解压的时间增多了。


4、优化内核

评估方法:

在启动参数里添加 initcall_debug,能得到更多内核 log:


另外,可以用 scripts/bootgraph.pl 将 dmesg 的信息转换成图片:

$ scripts/bootgraph.pl boot.log > boot.svg


接下来,找出消耗时间最多的环节,进行优化。

裁掉 tracing

在 Kernel hacking 里关闭 Tracers 相关的功能。

启动时间:缩短 550ms。

内核大小:缩小 217KB。

裁掉一些用不上的硬件功能

 

omap8250_platform_driver_init() // (660 ms)
cpsw_driver_init()  // (112 ms)
am335x_child_init() // (82 ms)
...


预设 loops per jiffy

在每次启动时,内核都会校准 delay loop 的值,用于 udelay() 函数。

这会测量 loops per jiffy (lpj) 的值。我们只需要启动一次内核,在log 查找 lpj 值:

Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)


然后将 lpj=4980736 填写到启动参数中,即可:

Calibrating delay loop (skipped) preset value.. 996.14 BogoMIPS (lpj=4980736)

大约缩短了 82 ms。

 

禁用 CONFIG_SMP

SMP 的初始化很慢。它通常在默认配置中是启用的,即使是一个单核 CPU。

如果我们的平台是单核的,可以禁用 SMP。

关闭后,内核缩小:-188 KB (-4.6 %),启动时间缩短 126ms.

 

禁用 log

启动参数里添加 quiet,启动时间缩短 577 ms。

禁用 CONFIG_PRINTK 和 CONFIG_BUG 后,内核缩小 118 KB (-5.8 %) 。

禁用 CONFIG_KALLSYMS 后,内核缩小 107 KB (-5.7 %) 。

合计,启动时间缩短 767 ms。

 

开启 CONFIG_EMBEDDED 和 CONFIG_EXPERT

这会让系统调用变得更精简,内核会变得没那么通用,但是能保持你的应用程序能运行就足够了。

内核缩小 51 KB。

启动时间缩短 34 ms。

 

选择 SLAB memory allocators

一般是 SLAB、SLOB、SLUB 三选一。

SLAB:默认选择,最通用、最传统、最可靠。

SLOB:更简洁,代码量更少,更节省空间,适合嵌入式系统,使能后,内核缩小 5 KB,但是启动时间增加 1.43 S!

SLUB:更合适大型系统,使能后,启动时间增加 2 ms。

因此,我们仍使用 SLAB。

 

内核压缩方式

不同压缩方式的特点如下:

Linux 启动时间优化实战,2.41 秒启动应用数据图表-heapdump性能社区


实测效果:

Linux 启动时间优化实战,2.41 秒启动应用数据图表-heapdump性能社区


看起来,gzip 和 lzo 表现更好。测试的效果应该是和 CPU/磁盘 的性能相关的。

 

内核编译参数

使能 CONFIG_CC_OPTIMIZE_FOR_SIZE,该选项可能是用 gcc -Os 代替 gcc -O2。

Linux 启动时间优化实战,2.41 秒启动应用数据图表-heapdump性能社区


注意,这只是在 BeagleBone Black + Linux 5.1 上的测试结果,不同平台之间有差异。

 

禁用 /proc 等伪文件系统

要考虑应用的兼容性。

ffmpeg 依赖 /proc ,所以只能关闭一些 proc 相关的选项:CONFIG_PROC_SYSCTL、CONFIG_PROC_PAGE_MONITOR CONFIG_CONFIGFS_FS,启动时间没有变化。

关闭 sysfs, 启动时间缩短 35 ms。

 

拼接 DTB

启用 CONFIG_ARM_APPENDED_DTB:

$ cat arch/arm/boot/zImage arch/arm/boot/dts/am335x-boneblack-lcd4.dtb > zImage

$ setenv bootcmd 'fatload mmc 0:1 81000000 zImage; bootz 81000000'


启动时间缩短 26 ms。


5、优化 Bootloader

这里我们采用最好的方案:使用 Uboot Falcon mode。

Falcon mode 只执行 Uboot 的第一阶段:SPL,然后跳过 Stage 2,执行加载 Kernel。

启动时间缩短 250 ms。


总结

到此,启动优化基本完成,最终效果如下:

[0.000000 0.000000]
[0.000785 0.000785] U-Boot SPL 2019.01 (Oct 27 2019 - 08:04:06 +0100)
[0.057822 0.057822] Trying to boot from MMC1
[0.378878 0.321056] fdt_root: FDT_ERR_BADMAGIC
[0.775306 0.396428] Waiting for /dev/video0 to be ready...
[1.966367 1.191061] Starting ffmpeg
...
[2.412284 0.004277] First frame decoded


从上电到 LCD 显示第一帧图像,总时间为 2.41 秒。

最有效果的步骤如下:

Linux 启动时间优化实战,2.41 秒启动应用数据图表-heapdump性能社区


仍值得优化的空间:

系统花了 1.2 秒等待 USB 摄像头的枚举,这里是否有办法加速?

是否可以关闭 tty 和终端登录?

最后,关于优化启动时间,有一些原则可以遵循:

请不要过早地进行优化。从一些影响面最小的点开始优化。从 rootfs 、kernel、bootloader 自上而下进行优化。重点关注短板。

感谢阅读,欢迎转发哦!

 

更多思考

Linux性能调优是运维工作经常遇到的问题,更多关于Linux的内容,大家可以阅读以下内容加深学习

火焰图:全局视野的Linux性能剖析

Linux虚拟内存,你理解到位了?

 

分类:
标签:
请先登录,再评论

👍

3周前

为你推荐

关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
LONG究竟有多长,从皇帝的新衣到海康SDK
转眼之间初中毕业30年了,但我仍清楚的记得初中英语的一篇课文,题目叫《皇帝的新装》(“The king’s new clothes”)。这篇课文的前两句话是:”Long long ago, there
谨防JDK8重复类定义造成的内存泄漏
概述 如今JDK8成了主流,大家都紧锣密鼓地进行着升级,享受着JDK8带来的各种便利,然而有时候升级并没有那么顺利?比如说今天要说的这个问题。我们都知道JDK8在内存模型上最大的改变是,放弃了Perm
JVM菜鸟进阶高手之路九(解惑)
关于MAT工具相关知识解惑MAT 不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如 Sun, HP, SAP 所采用的 HPROF 二进制堆存储文件,以及 IBM 的
JVM垃圾回收与一次线上内存泄露问题分析和解决过程
本文转载自:花椒技术微信公众号 前言内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。Ja
强如 Disruptor 也发生内存溢出?
前言```OutOfMemoryError ```问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界、空指针等)来说这类问题是很难定位和解决的。本文以最近碰到的一次线上内存溢出的定位、解决问题的
Linux 启动时间优化实战,2.41 秒启动应用!
导言快速启动嵌入式设备或电信设备,对于时间要求紧迫的应用程序是至关重要的,并且在改善用户体验方面也起着非常重要的作用。这个文章给予一些关于如何增强任意设备的启动时间的重要技巧。 正文 Hi, 我是老吴。今天看了一个关于启动优化的讲座,简单总结一下。本文的目标是尝试一