时间测量
说到性能调整,第一件该干的的事就是看下时间到底消耗在哪里。俗话说的好:知己知彼,百战百胜;过度优化,万恶之首
因此手头上要有称心如意的时间测试工具,方法。其实我是不太喜欢工具的,工具这东西可遇不可求,而且不如写代码顺手。
1. PRINTK_TIME
在内核编译选项中打开CONFIG_PRINTK_TIME,重新编译内核后,系统启动后就可以看到每一条printk前都有一个时间戳了,这样就有个大概的轮廓,哪
个驱动消耗了更多的时间,当然这个信息对我们性能调整来说粒度太粗了
2. initial_debug宏
上面的PRINTK_TIME只是在printk附加上当时的系统时间,对于那些没有printk信息输出的驱动来说是无法提供启动时间信息的,initial_debug派上了用场,打开这个宏后,每个驱动的初始化起始时间和结束时间都打印出来了。有了这个时间,基本就可以确定哪些部分需要优化了。我的做法是只关注耗时10000us以上的驱动。
3. ktime_t
调用ktime_get 获取时间,我最喜欢的时间测量方法,对于可疑的耗时函数,可如下测试
ktime_t start, end;
start = ktime_get()
candidate_func(arg...)
end = ktime_get()
printk(KERN_ALERT "%s: start time=%d.%d, end time=%d.%d\n", __func__, start.tv.sec, start.tv.nsec, end.tv.sec, end.tv.nsec);
可以很准确的计算candidate_func的时间,当然如果candidate_func中引发了调度,时间就没那么准确了。
4 printk
尽量去掉printk对时间测量的影响,可以调整kernel/printk.c中的DEFAULT_CONSOLE_LOGLEVEL宏,把级别较低的信息去掉
性能优化方法
1. hard code lpj,在uboot启动参数增加 lpj=3997696. 其中的3997696替换为你的机器启动信息获取的值,这个一般能节省200ms
2. 裁剪内核,这块比较大,要单独开一篇来介绍,裁剪的好处有两点:第一减少kernel的尺寸,这也就相应的减少了加载kernel image的时间,第二也减少了不必要的初始化
3. 代码调整:
根据时间测试,找到耗时瓶颈,看看是否能进行优化。
- 移除修改驱动中不必要的sleep 和delay
- 检查耗时的i2c操作,能否替换单个i2c寄存器读写为多个i2c寄存器读写,当然这需要硬件支持
- 推迟module init中不必要的操作到其他地方,比如open函数中,我觉得设备上下电,以及设备初始化都可以考虑移到初次使用设备时进行。
- 去掉hotplug helpers,因为android仅使用netlink来发送uevent事件。但是如果设置了hotplug helpers,那么当一个设备执行了uevent事件,就会从kernel尝试调用这个hotplug helper程序(即便这个helper程序并不存在),这个过程是很消耗时间的。
- 在某些场合,用mdelay替换init function中的msleep,由于嵌入式设备的HZ是100,因此msleep(1)导致系统调度后,需要10几ms才能重新调度回来,所以对于msleep 5ms以下都可以替换为mdelay
- 使用async_schedule调用module的probe函数,对于那些需要较多msleep的模块,这个方法可以通过并行提高init速度,但是一些模块对初始化顺序是有依赖的,所以慎用
4. 优化设备IO驱动,提高数据的读写速度,kernel启动完成到android launcher完成,几乎无处不涉及到文件的读写,IO速度对启动时间的影响重大,代码是人写的,所以平台特定的IO代码绝对有优化的空间