摘 ?要: EDKII作為當(dāng)前最流行的UEFI開(kāi)源實(shí)現(xiàn),其編譯工具性能的提升是非常關(guān)鍵也是值得研究的,同時(shí)編譯過(guò)程的快慢也是衡量這一框架的重要指標(biāo)。通過(guò)使用cProfile對(duì)EDKII Build工具編譯時(shí)間進(jìn)行分析,在Pre-build階段和Post-build階段進(jìn)行源代碼的修改,實(shí)現(xiàn)了通過(guò)比較編譯模塊Autogen相關(guān)文件時(shí)間戳是否改變的方式來(lái)提升增量編譯的性能和多線程產(chǎn)生平臺(tái)固件文件的方式來(lái)提升整體編譯的性能,采用這兩方面的改進(jìn),使得Build工具編譯達(dá)到更高的效率。最后在OVMF和KabyLake平臺(tái)的編譯過(guò)程中對(duì)上述兩個(gè)優(yōu)化方案進(jìn)行了檢測(cè)驗(yàn)證。
關(guān)鍵詞: UEFI;EDKII;Build;性能優(yōu)化
中圖分類號(hào): TP302.7 ? ?文獻(xiàn)標(biāo)識(shí)碼: A ? ?DOI:10.3969/j.issn.1003-6970.2020.06.039
本文著錄格式:朱勇洪. 關(guān)于EDKII Build工具的性能優(yōu)化研究[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 ?引言
軟件廠商可以根據(jù)UEFI[1]標(biāo)準(zhǔn)開(kāi)發(fā)屬于自己的UEFI實(shí)現(xiàn),其中最常用的開(kāi)源實(shí)現(xiàn)便是EDKII[2]。EDKII是遵循UEFI標(biāo)準(zhǔn)和PI[3]標(biāo)準(zhǔn)的跨平臺(tái)固件開(kāi)發(fā)環(huán)境,EDKII Build Tool是其中的平臺(tái)編譯工具。該工具目前正在被UEFI社區(qū)、開(kāi)發(fā)人員、測(cè)試人員及UEFI愛(ài)好者以日均數(shù)以幾千次甚至上萬(wàn)次的頻率在使用,對(duì)于一些簡(jiǎn)單平臺(tái)的編譯,該工具能夠很快的完成編譯,而對(duì)于一些復(fù)雜的服務(wù)器平臺(tái)的編譯,該工具往往需要20分鐘甚至半小時(shí)以上的編譯時(shí)間,那么這個(gè)效率就很難讓使用者滿意了。因而該工具的性能在使用過(guò)程中越來(lái)越被人們重視起來(lái)了。
本文通過(guò)cProfile對(duì)Build工具編譯耗費(fèi)時(shí)間比較長(zhǎng)的過(guò)程進(jìn)行分析,研究并實(shí)現(xiàn)了該工具在Pre-build階段和Post-build階段的一些優(yōu)化方案,有效的為整個(gè)平臺(tái)的編譯過(guò)程簡(jiǎn)省編譯時(shí)長(zhǎng),提升工具的整體性能。
1 ?EDKII Build過(guò)程簡(jiǎn)介
EDKII Build主要是用來(lái)處理EDKII meta-data文件(包括build_rule.txt, tools_def.txt, target.txt等),EDKII source和 binary文件,以及一些已經(jīng)存在的可兼容的EDK組件和庫(kù)模塊文件,最終產(chǎn)生遵循UEFI和PI規(guī)范的二進(jìn)制文件。
EDKII Build過(guò)程可以分為3個(gè)主要階段:
Pre-build(AutoGen)階段:主要是來(lái)解析meta-data文件,.dsc文件,.inf文件,.fdf文件,.dec文件,Unicode文件和VFR文件等,產(chǎn)生一些標(biāo)準(zhǔn)C的代碼文件(Autogen.c,Autogen.h)和相應(yīng)的Makefile文件[4]。
Build階段:主要是來(lái)處理source文件并通過(guò)Make(Linux系統(tǒng))或者Nmake(Windows系統(tǒng))來(lái)生成符合EFI格式的PE32/PE32+/COFF 文件[4]。
Post-build(ImageGen)階段:主要是來(lái)處理binary文件和EFI格式的文件,產(chǎn)生最終的UEFI Flash Images, Capsules,Applications和PCI Option ROMs[4]。
下圖1描述了這3個(gè)階段之間的關(guān)系。
在整個(gè)EDKII框架的編譯流程中涉及到了以下一些輸入文件:
.inf文件用于描述一個(gè)系統(tǒng)模塊(Module),它是UEFI系統(tǒng)的一個(gè)特色,其作用相當(dāng)于Visual Studio項(xiàng)目中的.proj文件,用于指導(dǎo)EDKII編譯工具自動(dòng)編譯模塊,它包含[Defines]、[Sources]、[Packages]、[LibraryClasses]等必須的部分以及 [Depex]、[Protocols]、[Ppis]、[Guids]、[PCD]、[BuildOptions]等可選的部分[5]。
.dsc文件用于描述一個(gè)平臺(tái)包(Package),它相當(dāng)于Visual Studio項(xiàng)目中的.solution文件,用于指導(dǎo)編譯工具編譯整個(gè)平臺(tái)包,它包含[Defines]、[LibararyClasses]、 [Components]等幾個(gè)必需部分以及 [PCD]、[BuildOptions]等幾個(gè)可選部分[6]。其中在[Components]內(nèi)定義的模塊都會(huì)被Build 工具編譯并生成對(duì)應(yīng)的.efi可執(zhí)行文件。
.dec文件是一個(gè)聲明文件,它定義了公開(kāi)的接口、數(shù)據(jù)及初始值,向其它模塊提供了本文件的資源。它包含了必須的[Defines]以及可選的[Includes]、[LibraryClasses]、[Guids]、[Protocols]、[Ppis]和[PCD]等幾個(gè)部分[7]。
.fdf文件是Flash的描述文件,描述了最終固件文件的組成結(jié)構(gòu),用于生成固件Image、Option Rom Image或可啟動(dòng)的Flash Image等[8]。
Meta-data文件中的build_rule.txt一般不需要用戶更改,編譯工具通過(guò)整個(gè)文件來(lái)定義如何處理不同的文件類型,如何構(gòu)建最終的固件Image文件等規(guī)則;而tools_def.txt則描述了用戶可配置的工具的路徑、參數(shù)等信息,以及一些默認(rèn)的編譯器標(biāo)記;target.txt描述了此次編譯的平臺(tái)名字、架構(gòu),以及build_rule.txt和tools_def.txt文件所在的路徑信息,因此該文件是編譯工具啟動(dòng)后第一個(gè)解析的。
2 ?性能優(yōu)化方案研究
整個(gè)Build工具的編譯過(guò)程復(fù)雜,涉及到的文件和相關(guān)工具的調(diào)用過(guò)程多,代碼量非常大,針對(duì)該工具性能的提升問(wèn)題,本文采用Python自帶的性能分析模塊cProfile[9],將其嵌入到Build工具的代碼中執(zhí)行,從而可以對(duì)得到的結(jié)果進(jìn)行分析。
例如: python -m cProfile -s time "`dirname $0`/../../Source/Python"/`basename $0`/`basename $0`.py $*
對(duì)整個(gè)平臺(tái)進(jìn)行編譯,可以得到如圖2的實(shí)驗(yàn)結(jié)果:
通過(guò)對(duì)cProfile得到的結(jié)果進(jìn)行callers分析,發(fā)現(xiàn)整個(gè)工具的編譯過(guò)程在AutoGen\AutoGen.py、AutoGen\BuildEngine.py和GenFds\FfsInfStatement.py花費(fèi)的時(shí)間尤其多,因此可以考慮對(duì)其進(jìn)行優(yōu)化,主要是在Pre-build階段和Post-build階段中。
2.1 ?Pre-build階段優(yōu)化
Pre-build階段通過(guò)解析不同的meta-data文件來(lái)獲取平臺(tái)相關(guān)的基本信息之后,就開(kāi)始解析各個(gè)不同的模塊的源文件來(lái)產(chǎn)生對(duì)應(yīng)的Autogen.c、Autogen.h、Makefile文件和.depex文件等[4]。在一個(gè)真實(shí)的平臺(tái)描述文件(.dsc)和Flash描述文件(.fdf)中,往往包含幾百個(gè)甚至上千個(gè)對(duì)應(yīng)的.inf文件需要解析,而每個(gè).inf文件又關(guān)聯(lián)著非常多的系統(tǒng)驅(qū)動(dòng)源文件和工程文件,從而導(dǎo)致整個(gè)平臺(tái)的編譯工程在Autogen階段需要花費(fèi)相當(dāng)長(zhǎng)的時(shí)間,這種情況在復(fù)雜的服務(wù)器平臺(tái)的編譯過(guò)程中尤為常見(jiàn)。
考慮一個(gè)很常見(jiàn)的例子,當(dāng)UEFI開(kāi)發(fā)人員對(duì)一個(gè)平臺(tái)進(jìn)行一次編譯之后,他后續(xù)對(duì)該平臺(tái)的某個(gè)系統(tǒng)驅(qū)動(dòng)程序進(jìn)行更改,可能只是更改某一個(gè)簡(jiǎn)單的.c 或者.h文件或者其它配置相關(guān)文件,此后當(dāng)他再次對(duì)整個(gè)平臺(tái)進(jìn)行重新編譯時(shí),這個(gè)時(shí)候的Autogen過(guò)程針對(duì)那些沒(méi)有被更改過(guò)的系統(tǒng)驅(qū)動(dòng)或者與被改動(dòng)的驅(qū)動(dòng)程序之間沒(méi)有存在依賴關(guān)系的系統(tǒng)驅(qū)動(dòng)而言是可以優(yōu)化的。然而,通過(guò)對(duì)cProfile得到的結(jié)果進(jìn)行分析后,發(fā)現(xiàn)當(dāng)前的Build工具并沒(méi)有做這樣的優(yōu)化,而是每次編譯都全部重新進(jìn)行產(chǎn)生。
本文正是采用這個(gè)思路,通過(guò)判斷來(lái)優(yōu)化掉沒(méi)有被更改的或者與被更改的驅(qū)動(dòng)文件不存在依賴關(guān)系的系統(tǒng)驅(qū)動(dòng)程序的重新編譯中的Autogen過(guò)程,以此來(lái)簡(jiǎn)省平臺(tái)在增量編譯過(guò)程中整個(gè)AutoGen階段的時(shí)間,從而達(dá)到平臺(tái)增量編譯過(guò)程的性能優(yōu)化。本文主要通過(guò)對(duì)EDKII Build工具Pre-build階段中AutoGen.py源文件進(jìn)行更改,在每一個(gè)編譯模塊進(jìn)行Autogen過(guò)程之后產(chǎn)生一個(gè)全新的文件AutoGenTimeStamp,這個(gè)新的文件中記錄了此次該模塊Autogen過(guò)程中所涉及到的所有文件,包括對(duì)整個(gè)平臺(tái)編譯都有影響的.dec文件,.dsc文件,.fdf文件,build_rule.txt,tools_def.txt,target.txt以及Build平臺(tái)時(shí)提供的全局參數(shù)等,也包括只與當(dāng)前模塊編譯相關(guān)的文件,其中包含有模塊本身的源文件和工程文件(如.inf文件,.c文件,.asm匯編文件,.uni字符串資源文件,.vfr窗體資源文件等),模塊依賴的庫(kù)模塊文件,編譯自動(dòng)生成的Autogen.h文件等。因此在用戶對(duì)某個(gè)或某些系統(tǒng)驅(qū)動(dòng)程序進(jìn)行更改之后,重新對(duì)該平臺(tái)進(jìn)行增量編譯時(shí),通過(guò)對(duì)已經(jīng)存在的AutoGenTimeStamp的分析比較便可以知道當(dāng)前的文件改動(dòng)對(duì)這個(gè)驅(qū)動(dòng)程序的重新編譯是否有影響,如果有影響,則該模塊的Autogen過(guò)程重新執(zhí)行一次,否則,可以直接優(yōu)化掉該模塊的Autogen過(guò)程,從而可以省略掉沒(méi)有被影響的模塊的Autogen過(guò)程,以此來(lái)減少整個(gè)平臺(tái)在增量編譯過(guò)程中 ? ?的Autogen時(shí)間。當(dāng)然,如果用戶此次改動(dòng)的是 ? ?一個(gè)全局的文件類型,如.dec文件,.dsc文件, build_rule.txt等,那么此次新的增量編譯過(guò)程中所有的AutoGenTimeStamp都會(huì)被更新,在這種情況下,這個(gè)優(yōu)化方案對(duì)整個(gè)平臺(tái)的增量編譯性能上是沒(méi)有額外提升的。除此之外,也可以考慮通過(guò)增加計(jì)算hash值的比較方式來(lái)進(jìn)行文件是否更改的 ? 判斷。
2.2 ?Post-build階段優(yōu)化
當(dāng)所有的模塊的.efi可執(zhí)行文件生成之后,就進(jìn)入了Post-build的階段。這個(gè)階段處理各個(gè)模塊的.efi可執(zhí)行文件以及一些Binary文件并通過(guò)特定的規(guī)則將其生成相應(yīng)的固件文件(Firmware files)、固件卷(Firmware volume)及最終的Flash image文件。整個(gè)流程如下圖3所示。
本文在對(duì)cProfile得到的結(jié)果并結(jié)合Build工具源代碼進(jìn)行分析后,發(fā)現(xiàn)目前EDKII Build工具在固件文件生成的時(shí)候是單線程執(zhí)行的,因此這個(gè)階段最大的性能優(yōu)化便在于如何實(shí)現(xiàn)多線程方式的產(chǎn)生平臺(tái)編譯所需的所有固件文件。
本文正是基于整個(gè)思路,實(shí)現(xiàn)多線程的方式產(chǎn)生固件文件來(lái)達(dá)到優(yōu)化平臺(tái)編譯的目的。通過(guò)對(duì)Pre-build階段、Build階段和Post-build階段的源代碼分析后,發(fā)現(xiàn)Pre-build階段是采用多線程的方式進(jìn)行文件的解析,Build階段也是通過(guò)多線程方式調(diào)用對(duì)應(yīng)的Makefile來(lái)產(chǎn)生此次平臺(tái)編譯所需的所有.efi可執(zhí)行文件,僅僅只有Post-build階段是單線程的依據(jù)系統(tǒng)驅(qū)動(dòng)程序在平臺(tái)描述文件(.dsc)和Flash描述文件(.fdf)中的順序進(jìn)行逐一產(chǎn)生對(duì)應(yīng)的固件文件。本文采用了一種最簡(jiǎn)便,代碼量更改較少的方式來(lái)進(jìn)行多線程的產(chǎn)生固件文件,首先通過(guò)更改Build階段中Makefile的生成過(guò)程,將每個(gè)系統(tǒng)驅(qū)動(dòng)程序的固件文件的生成過(guò)程也給寫到該模塊對(duì)應(yīng)的Makefile文件中,從而可以簡(jiǎn)便地利用現(xiàn)在已有的Build階段中的多線程機(jī)制進(jìn)行多線程的生成一個(gè)平臺(tái)編譯中所需要的所有固件文件,簡(jiǎn)省Post-build階段的編譯時(shí)間,最終簡(jiǎn)省平臺(tái)的整個(gè)編譯時(shí)間。其中涉及到Autogen過(guò)程中對(duì)Makefile文件的重新生成,Post-build階段中各個(gè)section(包括Apriori section,Compress section,Data section,Depex section,EFI section等)的重新生成過(guò)程以及Region的重新組成等復(fù)雜過(guò)程。該方法比直接考慮在現(xiàn)有EDKII框架下對(duì)Post-build階段代碼進(jìn)行重構(gòu)來(lái)實(shí)現(xiàn)多線程的方式更加容易實(shí)現(xiàn)。通過(guò)整個(gè)優(yōu)化后,發(fā)現(xiàn)編譯OVMF平臺(tái)在Post-build階段所花費(fèi)的時(shí)間直接從37 s降低到了8 s。
3 ?優(yōu)化方案的測(cè)試檢驗(yàn)
該檢驗(yàn)過(guò)程主要通過(guò)編譯OVMF(Open Virtual Machine Firmware)平臺(tái)和KabyLake平臺(tái)來(lái)查看Build 工具的編譯時(shí)間的變化來(lái)檢驗(yàn)性能是否有所優(yōu)化。OVMF是用于虛擬機(jī)上的UEFI固件,能夠較好的模擬真實(shí)環(huán)境,又可以做到快速、方便、簡(jiǎn)單。而KabyLake平臺(tái)是搭載Intel第七代酷睿處理器的真實(shí)平臺(tái)。
下表1和表2中的數(shù)據(jù)均是在相同環(huán)境下編譯五次后所得的有效數(shù)據(jù)的平均值,表1表示的是在一次編譯過(guò)程之后進(jìn)行系統(tǒng)某些驅(qū)動(dòng)文件更改后的增量編譯在Autogen階段所需要的時(shí)間,表2表示的是一次全新編譯過(guò)程總共所需要的時(shí)間。從表1 中可以看出,針對(duì)Pre-build階段的關(guān)于增量編譯的優(yōu)化方案可以提升27%-47%左右的性能,從表2中可以看出,針對(duì)Post-build階段的優(yōu)化方案可以提升15%-25%左右的性能。從而可以說(shuō)明Pre-build階段和Post-build階段所采用的優(yōu)化方案對(duì)編譯工具整體性能的提升是有效的,尤其是對(duì)一些編譯時(shí)間比較長(zhǎng)的服務(wù)器平臺(tái),其性能提升的效果是比較明顯的。
目前這兩個(gè)方案的優(yōu)化patch已經(jīng)提交到開(kāi)源的EDKII項(xiàng)目的github上去了。
4 ?結(jié)束語(yǔ)
本文通過(guò)cProfile模塊對(duì)Build工具性能進(jìn)行分析后,在Pre-build階段和Post-build階段分別通過(guò)比較編譯模塊Autogen相關(guān)文件時(shí)間戳是否發(fā)生改變的優(yōu)化和采用多線程方式產(chǎn)生各個(gè)驅(qū)動(dòng)文件的固件文件的優(yōu)化方案來(lái)提高整個(gè)編譯過(guò)程的性能。除此之外,也可以對(duì)編譯工具中的函數(shù)在一次編譯過(guò)程中的調(diào)用次數(shù)以及所花費(fèi)的時(shí)間來(lái)進(jìn)行分析,進(jìn)行函數(shù)優(yōu)化來(lái)達(dá)到提高性能的目的,例如ValueExpressionEx函數(shù)。
參考文獻(xiàn)
[1] UEFI Forum. UEFI Specification[OL].(2019-03) [2020-02]. http://www.uefi.org/specs/
[2] 戴正華. UEFI原理與編程[M]. 北京:機(jī)械工業(yè)出版社, 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.