关于EDKIIBuild工具的性能优化研究
2020-08-13朱勇洪
摘 要: EDKII作为当前最流行的UEFI开源实现,其编译工具性能的提升是非常关键也是值得研究的,同时编译过程的快慢也是衡量这一框架的重要指标。通过使用cProfile对EDKII Build工具编译时间进行分析,在Pre-build阶段和Post-build阶段进行源代码的修改,实现了通过比较编译模块Autogen相关文件时间戳是否改变的方式来提升增量编译的性能和多线程产生平台固件文件的方式来提升整体编译的性能,采用这两方面的改进,使得Build工具编译达到更高的效率。最后在OVMF和KabyLake平台的编译过程中对上述两个优化方案进行了检测验证。
关键词: UEFI;EDKII;Build;性能优化
中图分类号: TP302.7 文献标识码: A DOI:10.3969/j.issn.1003-6970.2020.06.039
本文著录格式:朱勇洪. 关于EDKII Build工具的性能优化研究[J]. 软件,2020,41(06):192195
【Abstract】: With the wide use of EDKII framework, the performance of Build tool becomes more and more important. Analyze the Build tools performance by cProfile, then update the code of Pre-build phase and Post-build phase, use the method to skip module Autogen by comparing timestamp and multiple thread to generate the firmware files to enhance the tools performance. In the end this enhancement method has been verified on OVMF and KabyLake platforms build process.
【Key words】: UEFI; EDKII; Build; Performance
0 引言
软件厂商可以根据UEFI[1]标准开发属于自己的UEFI实现,其中最常用的开源实现便是EDKII[2]。EDKII是遵循UEFI标准和PI[3]标准的跨平台固件开发环境,EDKII Build Tool是其中的平台编译工具。该工具目前正在被UEFI社区、开发人员、测试人员及UEFI爱好者以日均数以几千次甚至上万次的频率在使用,对于一些简单平台的编译,该工具能够很快的完成编译,而对于一些复杂的服务器平台的编译,该工具往往需要20分钟甚至半小时以上的编译时间,那么这个效率就很难让使用者满意了。因而该工具的性能在使用过程中越来越被人们重视起来了。
本文通过cProfile对Build工具编译耗费时间比较长的过程进行分析,研究并实现了该工具在Pre-build阶段和Post-build阶段的一些优化方案,有效的为整个平台的编译过程简省编译时长,提升工具的整体性能。
1 EDKII Build过程简介
EDKII Build主要是用来处理EDKII meta-data文件(包括build_rule.txt, tools_def.txt, target.txt等),EDKII source和 binary文件,以及一些已經存在的可兼容的EDK组件和库模块文件,最终产生遵循UEFI和PI规范的二进制文件。
EDKII Build过程可以分为3个主要阶段:
Pre-build(AutoGen)阶段:主要是来解析meta-data文件,.dsc文件,.inf文件,.fdf文件,.dec文件,Unicode文件和VFR文件等,产生一些标准C的代码文件(Autogen.c,Autogen.h)和相应的Makefile文件[4]。
Build阶段:主要是来处理source文件并通过Make(Linux系统)或者Nmake(Windows系统)来生成符合EFI格式的PE32/PE32+/COFF 文件[4]。
Post-build(ImageGen)阶段:主要是来处理binary文件和EFI格式的文件,产生最终的UEFI Flash Images, Capsules,Applications和PCI Option ROMs[4]。
下图1描述了这3个阶段之间的关系。
在整个EDKII框架的编译流程中涉及到了以下一些输入文件:
.inf文件用于描述一个系统模块(Module),它是UEFI系统的一个特色,其作用相当于Visual Studio项目中的.proj文件,用于指导EDKII编译工具自动编译模块,它包含[Defines]、[Sources]、[Packages]、[LibraryClasses]等必须的部分以及 [Depex]、[Protocols]、[Ppis]、[Guids]、[PCD]、[BuildOptions]等可选的部分[5]。
.dsc文件用于描述一个平台包(Package),它相当于Visual Studio项目中的.solution文件,用于指导编译工具编译整个平台包,它包含[Defines]、[LibararyClasses]、 [Components]等几个必需部分以及 [PCD]、[BuildOptions]等几个可选部分[6]。其中在[Components]内定义的模块都会被Build 工具编译并生成对应的.efi可执行文件。
.dec文件是一个声明文件,它定义了公开的接口、数据及初始值,向其它模块提供了本文件的资源。它包含了必须的[Defines]以及可选的[Includes]、[LibraryClasses]、[Guids]、[Protocols]、[Ppis]和[PCD]等几个部分[7]。
.fdf文件是Flash的描述文件,描述了最终固件文件的组成结构,用于生成固件Image、Option Rom Image或可启动的Flash Image等[8]。
Meta-data文件中的build_rule.txt一般不需要用户更改,编译工具通过整个文件来定义如何处理不同的文件类型,如何构建最终的固件Image文件等规则;而tools_def.txt则描述了用户可配置的工具的路径、参数等信息,以及一些默认的编译器标记;target.txt描述了此次编译的平台名字、架构,以及build_rule.txt和tools_def.txt文件所在的路径信息,因此该文件是编译工具启动后第一个解析的。
2 性能优化方案研究
整个Build工具的编译过程复杂,涉及到的文件和相关工具的调用过程多,代码量非常大,针对该工具性能的提升问题,本文采用Python自带的性能分析模块cProfile[9],将其嵌入到Build工具的代码中执行,从而可以对得到的结果进行分析。
例如: python -m cProfile -s time "`dirname $0`/../../Source/Python"/`basename $0`/`basename $0`.py $*
对整个平台进行编译,可以得到如图2的实验结果:
通过对cProfile得到的结果进行callers分析,发现整个工具的编译过程在AutoGen\AutoGen.py、AutoGen\BuildEngine.py和GenFds\FfsInfStatement.py花费的时间尤其多,因此可以考虑对其进行优化,主要是在Pre-build阶段和Post-build阶段中。
2.1 Pre-build阶段优化
Pre-build阶段通过解析不同的meta-data文件来获取平台相关的基本信息之后,就开始解析各个不同的模块的源文件来产生对应的Autogen.c、Autogen.h、Makefile文件和.depex文件等[4]。在一个真实的平台描述文件(.dsc)和Flash描述文件(.fdf)中,往往包含几百个甚至上千个对应的.inf文件需要解析,而每个.inf文件又关联着非常多的系统驱动源文件和工程文件,从而导致整个平台的编译工程在Autogen阶段需要花费相当长的时间,这种情况在复杂的服务器平台的编译过程中尤为常见。
考虑一个很常见的例子,当UEFI开发人员对一个平台进行一次编译之后,他后续对该平台的某个系统驱动程序进行更改,可能只是更改某一个简单的.c 或者.h文件或者其它配置相关文件,此后当他再次对整个平台进行重新编译时,这个时候的Autogen过程针对那些没有被更改过的系统驱动或者与被改动的驱动程序之间没有存在依赖关系的系统驱动而言是可以优化的。然而,通过对cProfile得到的结果进行分析后,发现当前的Build工具并没有做这样的优化,而是每次编译都全部重新进行产生。
本文正是采用这个思路,通过判断来优化掉没有被更改的或者与被更改的驅动文件不存在依赖关系的系统驱动程序的重新编译中的Autogen过程,以此来简省平台在增量编译过程中整个AutoGen阶段的时间,从而达到平台增量编译过程的性能优化。本文主要通过对EDKII Build工具Pre-build阶段中AutoGen.py源文件进行更改,在每一个编译模块进行Autogen过程之后产生一个全新的文件AutoGenTimeStamp,这个新的文件中记录了此次该模块Autogen过程中所涉及到的所有文件,包括对整个平台编译都有影响的.dec文件,.dsc文件,.fdf文件,build_rule.txt,tools_def.txt,target.txt以及Build平台时提供的全局参数等,也包括只与当前模块编译相关的文件,其中包含有模块本身的源文件和工程文件(如.inf文件,.c文件,.asm汇编文件,.uni字符串资源文件,.vfr窗体资源文件等),模块依赖的库模块文件,编译自动生成的Autogen.h文件等。因此在用户对某个或某些系统驱动程序进行更改之后,重新对该平台进行增量编译时,通过对已经存在的AutoGenTimeStamp的分析比较便可以知道当前的文件改动对这个驱动程序的重新编译是否有影响,如果有影响,则该模块的Autogen过程重新执行一次,否则,可以直接优化掉该模块的Autogen过程,从而可以省略掉没有被影响的模块的Autogen过程,以此来减少整个平台在增量编译过程中 的Autogen时间。当然,如果用户此次改动的是 一个全局的文件类型,如.dec文件,.dsc文件, build_rule.txt等,那么此次新的增量编译过程中所有的AutoGenTimeStamp都会被更新,在这种情况下,这个优化方案对整个平台的增量编译性能上是没有额外提升的。除此之外,也可以考虑通过增加计算hash值的比较方式来进行文件是否更改的 判断。
2.2 Post-build阶段优化
当所有的模块的.efi可执行文件生成之后,就进入了Post-build的阶段。这个阶段处理各个模块的.efi可执行文件以及一些Binary文件并通过特定的规则将其生成相应的固件文件(Firmware files)、固件卷(Firmware volume)及最终的Flash image文件。整个流程如下图3所示。
本文在对cProfile得到的结果并结合Build工具源代码进行分析后,发现目前EDKII Build工具在固件文件生成的时候是单线程执行的,因此这个阶段最大的性能优化便在于如何实现多线程方式的产生平台编译所需的所有固件文件。
本文正是基于整个思路,实现多线程的方式产生固件文件来达到优化平台编译的目的。通过对Pre-build阶段、Build阶段和Post-build阶段的源代码分析后,发现Pre-build阶段是采用多线程的方式进行文件的解析,Build阶段也是通过多线程方式调用对应的Makefile来产生此次平台编译所需的所有.efi可执行文件,仅仅只有Post-build阶段是单线程的依据系统驱动程序在平台描述文件(.dsc)和Flash描述文件(.fdf)中的顺序进行逐一产生对应的固件文件。本文采用了一种最简便,代码量更改较少的方式来进行多线程的产生固件文件,首先通过更改Build阶段中Makefile的生成过程,将每个系统驱动程序的固件文件的生成过程也给写到该模块对应的Makefile文件中,从而可以简便地利用现在已有的Build阶段中的多线程机制进行多线程的生成一个平台编译中所需要的所有固件文件,简省Post-build阶段的编译时间,最终简省平台的整个编译时间。其中涉及到Autogen过程中对Makefile文件的重新生成,Post-build阶段中各个section(包括Apriori section,Compress section,Data section,Depex section,EFI section等)的重新生成過程以及Region的重新组成等复杂过程。该方法比直接考虑在现有EDKII框架下对Post-build阶段代码进行重构来实现多线程的方式更加容易实现。通过整个优化后,发现编译OVMF平台在Post-build阶段所花费的时间直接从37 s降低到了8 s。
3 优化方案的测试检验
该检验过程主要通过编译OVMF(Open Virtual Machine Firmware)平台和KabyLake平台来查看Build 工具的编译时间的变化来检验性能是否有所优化。OVMF是用于虚拟机上的UEFI固件,能够较好的模拟真实环境,又可以做到快速、方便、简单。而KabyLake平台是搭载Intel第七代酷睿处理器的真实平台。
下表1和表2中的数据均是在相同环境下编译五次后所得的有效数据的平均值,表1表示的是在一次编译过程之后进行系统某些驱动文件更改后的增量编译在Autogen阶段所需要的时间,表2表示的是一次全新编译过程总共所需要的时间。从表1 中可以看出,针对Pre-build阶段的关于增量编译的优化方案可以提升27%-47%左右的性能,从表2中可以看出,针对Post-build阶段的优化方案可以提升15%-25%左右的性能。从而可以说明Pre-build阶段和Post-build阶段所采用的优化方案对编译工具整体性能的提升是有效的,尤其是对一些编译时间比较长的服务器平台,其性能提升的效果是比较明显的。
目前这两个方案的优化patch已经提交到开源的EDKII项目的github上去了。
4 结束语
本文通过cProfile模块对Build工具性能进行分析后,在Pre-build阶段和Post-build阶段分别通过比较编译模块Autogen相关文件时间戳是否发生改变的优化和采用多线程方式产生各个驱动文件的固件文件的优化方案来提高整个编译过程的性能。除此之外,也可以对编译工具中的函数在一次编译过程中的调用次数以及所花费的时间来进行分析,进行函数优化来达到提高性能的目的,例如ValueExpressionEx函数。
参考文献
[1] UEFI Forum. UEFI Specification[OL].(2019-03) [2020-02]. http://www.uefi.org/specs/
[2] 戴正华. UEFI原理与编程[M]. 北京:机械工业出版社, 2015. 1: 14.
[3] UEFI Forum. UEFI Platform Initialization Specification [OL].(2019-01) [2020-02].http://www.uefi.org/specs/.
[4] Intel Corporation. EDK II Build Specification[OL].(2018-04) [2020-02].https://edk2-docs.gitbooks.io/edk-ii-build-specification/content/v/release/1.28/.
[5] Intel Corporation. EDK II INF Specification[OL].(2018-04) [2020-02].https://edk2-docs.gitbooks.io/edk-ii-inf-specification/content/v/release/1.27/.
[6] Intel Corporation. EDK II DSC Specification[OL].(2018-04) [2020-02]. https://edk2-docs.gitbooks.io/edk-ii-dsc-specification/ content/v/release/1.28/.
[7] Intel Corporation. EDK II DEC Specification[OL].(2018-04) [2020-02].https://edk2-docs.gitbooks.io/edk-ii-dec-specification/content/v/release/1.27/.
[8] Intel Corporation. EDK II FDF Specification[OL].(2017-06) [2020-02].https://edk2-docs.gitbooks.io/edk-ii-fdf-specification/content/v/release/1.28/.
[9] Python Software Foundation. The Python Profilers[OL]. (2019-06) [2020-02]。https://docs.python.org/3/library/profile.html.