魏 光,錢(qián)德沛,2,楊海龍,2,欒鐘治,2+
1.北京航空航天大學(xué) 計(jì)算機(jī)學(xué)院 中德聯(lián)合軟件研究所,北京 100191
2.北京航空航天大學(xué) 軟件開(kāi)發(fā)環(huán)境國(guó)家重點(diǎn)實(shí)驗(yàn)室,北京 100191
在“新基建”的推動(dòng)下,數(shù)據(jù)中心等基礎(chǔ)設(shè)施發(fā)展迅猛,對(duì)電力能源的需求快速增長(zhǎng)。數(shù)據(jù)中心中存在大量的服務(wù)器等IT 設(shè)備,每天都要處理海量數(shù)據(jù),完成大量計(jì)算,隨著能源價(jià)格的不斷上升,電費(fèi)已經(jīng)成為數(shù)據(jù)中心最主要的運(yùn)營(yíng)成本。另一方面,隨著移動(dòng)互聯(lián)網(wǎng)應(yīng)用的普及,電源受限的移動(dòng)或嵌入式設(shè)備的能耗問(wèn)題也引起越來(lái)越多的關(guān)注。在電源受限的設(shè)備例如手機(jī)中,軟件能耗優(yōu)化的重要性日益凸顯,優(yōu)化這類(lèi)軟件的能耗不僅可以節(jié)能,更重要的是可以延長(zhǎng)設(shè)備的待機(jī)或操作時(shí)間。因此,IT設(shè)備的節(jié)能降耗已經(jīng)成為學(xué)術(shù)界和工業(yè)界共同關(guān)注的熱點(diǎn)問(wèn)題。
在降低IT 設(shè)備能耗方面已有大量研究工作,例如,縮小集成電路特征尺寸降低供電電壓,改進(jìn)器件結(jié)構(gòu)降低靜態(tài)電流,以分區(qū)供電應(yīng)對(duì)工藝的離散性,動(dòng)態(tài)調(diào)整電壓頻率以降低運(yùn)行功耗,沉浸式相變高效冷卻,以及能耗感知的任務(wù)調(diào)度等。但是,從軟件開(kāi)發(fā)和程序編寫(xiě)模式角度研究如何降低能耗的工作還比較少。數(shù)據(jù)中心存在大量每天都要反復(fù)運(yùn)行的日常應(yīng)用軟件,如果能從改進(jìn)這些軟件的編寫(xiě)模式和數(shù)據(jù)訪問(wèn)模式入手,使這些軟件在相同硬件條件下的運(yùn)行能耗得到降低,那么數(shù)據(jù)中心的整體能效就會(huì)得到進(jìn)一步改善。優(yōu)化程序能耗的關(guān)鍵在于定位程序中能耗熱點(diǎn),發(fā)現(xiàn)產(chǎn)生過(guò)高能耗的原因,通過(guò)改變代碼的編寫(xiě)方式和數(shù)據(jù)的訪問(wèn)方式,達(dá)到降低程序執(zhí)行能耗的目的,稱(chēng)這種編程模式為能耗感知的編程(energy-aware programming,EAP)。如何在程序開(kāi)發(fā)過(guò)程中準(zhǔn)確測(cè)量程序代碼的能耗,建立起能耗、性能事件和程序代碼三者之間的關(guān)聯(lián)關(guān)系,是實(shí)現(xiàn)能耗感知編程的關(guān)鍵。
本文針對(duì)能耗感知編程的需求,提出一種程序能耗和性能事件協(xié)同測(cè)量與分析的方法,通過(guò)統(tǒng)一的時(shí)間基準(zhǔn),建立程序能耗熱點(diǎn)、性能事件和程序代碼段之間的關(guān)聯(lián)關(guān)系,確定影響程序能耗的主要因素,定位與高能耗對(duì)應(yīng)的程序操作,從而為面向能耗的代碼優(yōu)化奠定基礎(chǔ)。在論述方法的基本原理之后,本文簡(jiǎn)要介紹了基于該方法的程序能耗測(cè)量分析工具FPowerTool的實(shí)現(xiàn)以及能耗和性能事件關(guān)聯(lián)分析的方法。然后,以若干典型程序的能耗優(yōu)化為例,分析程序能耗與代碼編寫(xiě)模式、數(shù)據(jù)存放和訪問(wèn)模式等因素之間的關(guān)系,通過(guò)改變程序中與過(guò)高能耗相關(guān)的變量定義、賦值和訪問(wèn)模式,降低程序執(zhí)行的能耗。實(shí)驗(yàn)結(jié)果表明,本文提出的能耗與性能事件協(xié)同測(cè)量與分析方法能夠準(zhǔn)確地獲取程序的能耗行為,建立能耗與性能事件之間的關(guān)系,幫助程序員分析影響程序能耗的主要因素,從而有針對(duì)性地改進(jìn)代碼,優(yōu)化程序的能耗,并可以利用FPowerTool工具評(píng)估對(duì)比代碼修改前后能效的優(yōu)劣。
大部分現(xiàn)代處理器都集成了RAPL(running average power limit)模塊。RAPL 使用了軟件的能耗模型,提供了讀取事件編碼及掩碼配置MSR(model specific register)接口。Intel 的技術(shù)文檔中說(shuō)明RAPL 相關(guān)計(jì)數(shù)器的更新頻率為1 kHz,這對(duì)于能耗的精準(zhǔn)測(cè)量是一個(gè)限制。MSR 中包含了系統(tǒng)中PKG(package)、PP0(power plane 0)、PP1(power plane 1)和DRAM 能量消耗的計(jì)數(shù)值。其中PKG 表示整個(gè)芯片,PP0 表示處理器的所有核,PP1 表示其他非處理器的設(shè)備(一般是GPU),DRAM 表示主存。通過(guò)MSR 還可以獲得性能計(jì)數(shù)器的值,目前已經(jīng)有多種成熟的采樣性能計(jì)數(shù)器的工具被廣泛使用,例如PAPI(performance application programming interface)、Perfsuit、Perfmon和libpfm4 函數(shù)庫(kù)等。Perf是Linux 內(nèi)核中的一個(gè)功能強(qiáng)大的性能調(diào)優(yōu)工具,可以通過(guò)RAPL 提供的接口進(jìn)行能耗的測(cè)量。Likwid是Linux 操作系統(tǒng)對(duì)Intel 和AMD 處理器進(jìn)行性能監(jiān)控的工具集,其中的likwid-powermeter 工具可以讀取RAPL 中的能耗信息,計(jì)算出程序運(yùn)行過(guò)程中系統(tǒng)消耗的能量。
一些能耗研究工作結(jié)合使用性能分析工具和RAPL 來(lái)分析應(yīng)用的能耗信息。Khan 等人基于IgProf的能耗工具和RAPL,得到了應(yīng)用執(zhí)行時(shí)間和特定函數(shù)所消耗的能量的強(qiáng)相關(guān)性信息。TProf是一個(gè)并行程序能耗的評(píng)價(jià)工具,其中能耗部分的測(cè)量使用PAPI 來(lái)讀取RAPL 的值。Mukhanov 等人設(shè)計(jì)的ALEA 是一個(gè)細(xì)粒度的能耗評(píng)價(jià)工具,通過(guò)建立能耗的統(tǒng)計(jì)模型并利用RAPL 計(jì)算得到代碼基本塊的能耗值,并在Intel Sandy Bridge 和ARM big.LITTLE硬件平臺(tái)上進(jìn)行了驗(yàn)證。E-Team是一個(gè)Linux上基于RAPL 測(cè)量能耗信息的調(diào)度器。
面向代碼的能耗優(yōu)化按優(yōu)化的對(duì)象可分為指令級(jí)、語(yǔ)句級(jí)和模塊級(jí)。面向語(yǔ)句級(jí)的能耗優(yōu)化更適合于程序的開(kāi)發(fā)或調(diào)試過(guò)程,例如通過(guò)代碼結(jié)構(gòu)或數(shù)據(jù)結(jié)構(gòu)的變換、提高緩存利用率等方法來(lái)降低能耗。Kandemir 等人在多體存儲(chǔ)系統(tǒng)(multi-bank memory systems)上研究了循環(huán)優(yōu)化對(duì)能耗的影響,例如循環(huán)分裂融合(loop fission and fusion)、循環(huán)分塊(loop tiling)和線性循環(huán)變換(linear loop transformation),這些方法嘗試提高緩存利用率,但僅使用建模的方法來(lái)得到能耗信息,并未測(cè)得代碼在硬件上的實(shí)際能耗信息。Bunse 等人在嵌入式環(huán)境下評(píng)測(cè)了各種排序算法的能效,發(fā)現(xiàn)不同的算法消耗的能量不同,但一個(gè)算法的時(shí)間復(fù)雜度和能量消耗之間并無(wú)必然的聯(lián)系,實(shí)驗(yàn)中采用外接數(shù)字示波器的方法來(lái)估算能耗。還有一些研究工作使用系統(tǒng)硬件計(jì)數(shù)器來(lái)進(jìn)行能耗建模,文獻(xiàn)[22-23]歸納總結(jié)了在數(shù)據(jù)中心和高性能計(jì)算系統(tǒng)能耗及應(yīng)用能耗建模和預(yù)測(cè)方面的工作。
高速緩存(cache memory)是介于處理器與內(nèi)存之間的存儲(chǔ)層次,對(duì)于程序員并不顯式可見(jiàn),但對(duì)于提高處理器性能至關(guān)重要。顯然,訪問(wèn)處理器片外的內(nèi)存要比訪問(wèn)片內(nèi)的高速緩存慢得多。cache 對(duì)提高處理器性能非常重要,但由于是用高速靜態(tài)存儲(chǔ)器技術(shù)實(shí)現(xiàn)的,其本身能耗也很高。研究工作表明,ARM 920T 的cache 子系統(tǒng)的能耗約占處理器整體能耗的44%。另一研究工作對(duì)不同規(guī)模的處理器進(jìn)行了模擬,發(fā)現(xiàn)因泄漏電流導(dǎo)致的靜態(tài)能耗比例約為37%~72%,其中L2 cache 產(chǎn)生了大部分靜態(tài)能耗。因此,改進(jìn)cache 的組織結(jié)構(gòu)、一致性協(xié)議和替換策略不僅能提高處理器性能,也會(huì)降低處理器能耗。在改進(jìn)cache 優(yōu)化程序執(zhí)行性能方面已有大量研究,本文將從cache 中數(shù)據(jù)的放置以及訪問(wèn)模式角度分析cache對(duì)程序能耗的影響。
圖1 顯示了能耗感知編程的過(guò)程。能耗和性能事件測(cè)量模塊采集原始代碼執(zhí)行過(guò)程中消耗的能量和發(fā)生的性能事件,產(chǎn)生與被測(cè)代碼執(zhí)行過(guò)程相對(duì)應(yīng)的能耗數(shù)據(jù)和性能事件數(shù)據(jù);采集到的數(shù)據(jù)經(jīng)過(guò)能耗分析模塊處理,得到能耗與性能事件的相關(guān)關(guān)系。再由能耗優(yōu)化模塊根據(jù)相關(guān)性,改寫(xiě)程序代碼編寫(xiě)或數(shù)據(jù)訪問(wèn)模式,減少能耗高的性能事件,得到能耗優(yōu)化的代碼。這個(gè)過(guò)程可以多次迭代,直到達(dá)到預(yù)期的效果為止。
圖1 能耗感知編程的過(guò)程Fig.1 Workflow of energy-aware programming
能耗感知編程需要解決以下問(wèn)題:
(1)如何以較細(xì)粒度測(cè)量代碼段執(zhí)行產(chǎn)生的能耗,精確定位程序中的能耗熱點(diǎn)。
(2)如何確定程序能耗和程序執(zhí)行中性能事件之間的因果關(guān)系,找出影響能耗的主要因素。
(3)如何確定能耗熱點(diǎn)程序段中的性能事件與代碼編寫(xiě)模式和數(shù)據(jù)放置與訪問(wèn)模式之間的關(guān)系,為面向能耗的代碼優(yōu)化提供依據(jù)和指導(dǎo)。
針對(duì)上述能耗感知編程急需解決的問(wèn)題,提出了一種程序能耗和性能事件協(xié)同測(cè)量與分析的新方法EPC(energy-performance correlation)。EPC 的基本原理是,在程序執(zhí)行過(guò)程中同時(shí)采集消耗的能量和產(chǎn)生的性能事件數(shù)據(jù),通過(guò)基于統(tǒng)一時(shí)間基準(zhǔn)的時(shí)間戳把代碼段產(chǎn)生的能耗和性能事件關(guān)聯(lián)起來(lái),建立它們之間的對(duì)應(yīng)關(guān)系。能耗的測(cè)量通過(guò)采集處理器內(nèi)置的能量消耗計(jì)數(shù)器完成,不需額外的測(cè)量硬件,也無(wú)需對(duì)硬件做任何改動(dòng)。在測(cè)量過(guò)程中,始終有一個(gè)能耗采集模塊在后臺(tái)運(yùn)行,周期性地采集系統(tǒng)的能耗值,附加上采集時(shí)刻的時(shí)間戳后形成系統(tǒng)能耗文件并存儲(chǔ)。這個(gè)模塊的作用相當(dāng)于一塊虛擬電表,記錄系統(tǒng)消耗的電能,供測(cè)量時(shí)查詢(xún)。被測(cè)程序被劃分基本代碼段。一個(gè)基本代碼段可以是函數(shù)或任意大小的代碼段,通過(guò)插樁的方法在基本代碼段的開(kāi)始點(diǎn)和結(jié)束點(diǎn)插入簡(jiǎn)短的代碼。插入的代碼從系統(tǒng)能耗采集模塊獲得該基本代碼段消耗的能量值,并從處理器內(nèi)置的硬件性能計(jì)數(shù)器獲取該代碼段執(zhí)行過(guò)程中產(chǎn)生的性能事件值。采集到的數(shù)據(jù)附加時(shí)間戳后存儲(chǔ),供后續(xù)分析時(shí)使用。通過(guò)比對(duì)時(shí)間戳,把一個(gè)基本代碼段的能耗、性能事件和基本代碼段在源代碼中的位置關(guān)聯(lián)起來(lái),從而為分析產(chǎn)生高能耗的主要因素,優(yōu)化程序代碼提供依據(jù)。
基于EPC 的基本原理,設(shè)計(jì)實(shí)現(xiàn)了一個(gè)支持能耗感知與分析的評(píng)測(cè)工具FPowerTool。FPowerTool以插樁和采樣相結(jié)合的方法,動(dòng)態(tài)獲取處理器內(nèi)置的能量計(jì)數(shù)器和性能事件計(jì)數(shù)器的值,從而準(zhǔn)確地測(cè)量各個(gè)基本代碼段在執(zhí)行過(guò)程中所消耗的能量和產(chǎn)生的性能事件,分析基本代碼段的能耗行為與其產(chǎn)生的性能事件的關(guān)系,掌握程序的能耗行為特征。FPowerTool的實(shí)現(xiàn)有以下特點(diǎn):
評(píng)測(cè)粒度可控:基本代碼段的大小對(duì)于能耗熱點(diǎn)定位的準(zhǔn)確性有直接影響。代碼段劃分得越小,越有利于能耗熱點(diǎn)的定位。但如果基本代碼段過(guò)小,測(cè)量開(kāi)銷(xiāo)過(guò)大,反而會(huì)造成被測(cè)程序能耗行為的失真。在FPowerTool 的實(shí)現(xiàn)中,基本代碼段可以是一個(gè)函數(shù),也可以是指定的代碼片段。通過(guò)指定特定被測(cè)函數(shù)或調(diào)節(jié)基本代碼段的大小,F(xiàn)PowerTool可以控制測(cè)量的粒度。實(shí)際使用中,可以先劃分較大的基本代碼段,粗略定位熱點(diǎn),然后進(jìn)一步細(xì)分基本代碼段,獲得更為精細(xì)的熱點(diǎn)能耗行為。
非入侵性:能耗的測(cè)量對(duì)非侵入要求更苛刻,不能改變程序原有的時(shí)序關(guān)系和能耗行為,造成測(cè)量的失真。FPowerTool 通過(guò)對(duì)編譯后的二進(jìn)制可執(zhí)行文件添加動(dòng)態(tài)插樁,實(shí)現(xiàn)對(duì)基本代碼段能耗及性能事件數(shù)據(jù)的采集,動(dòng)態(tài)插樁的位置位于函數(shù)的調(diào)用和返回處,或基本代碼段的起始行和結(jié)束行。FPower-Tool不需要對(duì)被測(cè)程序源代碼做任何修改,只需在被測(cè)程序編譯時(shí)使用“-g”編譯選項(xiàng),以獲得基本代碼段的行號(hào)信息。非入侵性意味著FPowerTool 能耗測(cè)量過(guò)程中引入的失真較小,不會(huì)影響被測(cè)程序執(zhí)行的正確性。
低開(kāi)銷(xiāo):由于不需要對(duì)被測(cè)程序做預(yù)處理和靜態(tài)檢測(cè),F(xiàn)PowerTool 引入的額外開(kāi)銷(xiāo)很小。通過(guò)控制動(dòng)態(tài)插樁的探針數(shù)量以及合理選擇基本代碼段的大小,可以在開(kāi)銷(xiāo)可控的前提下完成對(duì)指定代碼范圍內(nèi)所需粒度的測(cè)量,在測(cè)量過(guò)程中對(duì)被測(cè)程序執(zhí)行時(shí)間的影響很小。
FPowerTool 的動(dòng)態(tài)插樁主要基于SystemTap 實(shí)現(xiàn)。SystemTap 是一個(gè)基于Linux 內(nèi)核的調(diào)試和監(jiān)控工具。使用SystemTap 腳本可以對(duì)二進(jìn)制可執(zhí)行程序添加動(dòng)態(tài)插入的探針。SystemTap 腳本會(huì)被翻譯成C 代碼,然后轉(zhuǎn)換成一個(gè)系統(tǒng)內(nèi)核模塊。通過(guò)在內(nèi)核層面的uprobes 接口,對(duì)代碼進(jìn)行動(dòng)態(tài)插樁。當(dāng)可執(zhí)行文件執(zhí)行到探針的探測(cè)點(diǎn)時(shí),模塊會(huì)被觸發(fā)并執(zhí)行相應(yīng)的操作。動(dòng)態(tài)插樁的流程圖如圖2 所示。
圖2 動(dòng)態(tài)插樁流程圖Fig.2 Workflow of dynamic instrument
在動(dòng)態(tài)插樁過(guò)程中,在基本代碼段首尾成對(duì)地插入探針,對(duì)非函數(shù)的任意代碼段的動(dòng)態(tài)插樁而言,額外需要DWARF(debugging with attributed record formats)調(diào)試信息中的行號(hào)。簡(jiǎn)言之,DWARF 信息在動(dòng)態(tài)插樁的探測(cè)點(diǎn)和對(duì)應(yīng)的源代碼行號(hào)之間建立起關(guān)聯(lián),使得可執(zhí)行文件執(zhí)行到指定行數(shù)時(shí)會(huì)觸發(fā)相應(yīng)的探針。
當(dāng)插樁的探針被觸發(fā)時(shí),SystemTap 工具就會(huì)記錄下觸發(fā)的時(shí)刻和perf 事件的統(tǒng)計(jì)信息。圖3 展示了被測(cè)程序插樁的探針被觸發(fā)的示意圖。
圖3 程序插樁觸發(fā)示意圖Fig.3 Schematic diagram of probe triggering
圖4 一個(gè)基本代碼段的能耗計(jì)算Fig.4 Computing energy consumption by a basic code block
虛擬電表程序即能耗采集后臺(tái)程序通過(guò)調(diào)用PAPI 周期性地讀取RPAL 能耗數(shù)據(jù)。在被測(cè)程序執(zhí)行過(guò)程中,F(xiàn)PowerTool 會(huì)不停地記錄程序執(zhí)行過(guò)程中各個(gè)基本代碼段的時(shí)間信息和RAPL 相關(guān)的能耗數(shù)據(jù)。程序執(zhí)行結(jié)束后,F(xiàn)PowerTool 會(huì)根據(jù)時(shí)間戳把基本代碼段的插樁信息和能耗數(shù)據(jù)相互關(guān)聯(lián)起來(lái)。圖4 是計(jì)算一個(gè)基本代碼段能耗信息的示意圖。圖中左側(cè)是插樁產(chǎn)生的基本代碼段時(shí)間戳信息,右側(cè)是能耗采集模塊產(chǎn)生的采樣時(shí)刻信息和能耗數(shù)值。因?yàn)槟芎牟蓸幽K和基本代碼段的動(dòng)態(tài)插樁都是利用SystemTap 工具在探針觸發(fā)時(shí)記錄的時(shí)間戳信息,也就是說(shuō),基本代碼段的時(shí)間戳信息和能耗采樣的時(shí)間戳信息是基于同一時(shí)間基準(zhǔn),因此可以使用基本代碼段開(kāi)始與結(jié)束處的兩個(gè)時(shí)間戳作為索引,精確計(jì)算并累加這兩個(gè)時(shí)間點(diǎn)之間的能耗采樣數(shù)據(jù)值,不足一個(gè)RAPL 能耗采樣時(shí)間間隔的部分按時(shí)間比例計(jì)算。計(jì)算方法如式(1)所示,其中表示插樁過(guò)程中的能耗,和表示插樁在開(kāi)始處和結(jié)束處的時(shí)間戳,E表示時(shí)間戳t和t間的能耗。
更為詳盡的FPowerTool設(shè)計(jì)與實(shí)現(xiàn)可參閱文獻(xiàn)[1]和Github(https://github.com/fpowertool/FPowerTool)。
EPC 方法不僅實(shí)現(xiàn)對(duì)基本代碼段的能耗的測(cè)量,還同時(shí)記錄了基本代碼段執(zhí)行過(guò)程中產(chǎn)生的性能事件數(shù)值,例如緩存命中和撲空、內(nèi)存訪問(wèn)以及I/O 操作的次數(shù)等。這些性能事件信息為識(shí)別能耗與各種性能事件之間的關(guān)系,分析過(guò)高能耗的原因,進(jìn)而優(yōu)化相應(yīng)的代碼提供了依據(jù)。為了分析性能事件與程序能耗之間的關(guān)系,使用機(jī)器學(xué)習(xí)中的判別分析方法建立了以性能事件為輸入變量、以能耗水平為輸出的能耗模型。模型由一組判別函數(shù)和一組能耗分類(lèi)的重心(坐標(biāo))組成。判別函數(shù)是性能事件測(cè)量值的一個(gè)線性組合,其一般形式如式(2)所示,其中a為系數(shù),x為第個(gè)性能事件的值。在本文的模型中,選取了20種性能事件作為輸入變量,如表1所示。
表1 模型用到的性能事件Table 1 Performance events used in model
使用PARSEC和NPB 3.3 基準(zhǔn)測(cè)試集中的22個(gè)程序,在不同數(shù)據(jù)規(guī)模和不同線程數(shù)的組合下運(yùn)行,得到900 多個(gè)樣本,每個(gè)樣本含一個(gè)能耗值和20個(gè)性能事件值。利用這些樣本,訓(xùn)練出14 個(gè)判別函數(shù)。程序能耗由低至高分為15 級(jí)。預(yù)測(cè)一個(gè)新的程序的能耗時(shí),使用該程序的性能事件值作為輸入,計(jì)算出14 個(gè)判別函數(shù)的輸出分?jǐn)?shù),然后以這14 個(gè)分?jǐn)?shù)得到該程序在判別空間中的坐標(biāo),與各能耗分類(lèi)的重心相比較,判斷該程序落在哪個(gè)分類(lèi)。判別分析的原理在此不再贅述,可參見(jiàn)文獻(xiàn)[30]。
根據(jù)標(biāo)準(zhǔn)化后的判別函數(shù)公式中性能事件變量系數(shù)的大小,就可以初步判定各種性能事件對(duì)于能耗的相對(duì)重要性。判別函數(shù)的特征值表明其判別能力,特征值越大,判別能力越強(qiáng)。其中建立的第一個(gè)判別函數(shù)就具有89.945%的判別能力,前兩個(gè)判別函數(shù)就可以解釋96%的變量。因此,分析第一個(gè)判別函數(shù)中變量的系數(shù),就可以大致了解各種性能事件對(duì)能耗的影響。表2 顯示了第一個(gè)判別函數(shù)中各變量(性能事件)的系數(shù)??梢钥闯?,在同一個(gè)架構(gòu)下影響程序能耗水平的主要因素是與CPU 相關(guān)的性能事件,如cpucycles,降低指令執(zhí)行次數(shù)就可以降低能耗。其次,緩存相關(guān)的性能事件,如iTLBloads 和L1icacheloadmisses 等也有較大影響。了解各個(gè)性能事件對(duì)于能耗的重要性有助于找出影響能耗的主要因素,對(duì)代碼的改寫(xiě)和優(yōu)化有指導(dǎo)意義。
表2 第一個(gè)判別函數(shù)中各性能事件的相對(duì)重要性Table 2 Relative importance of performance events in the first discriminant function
為了驗(yàn)證FPowerTool 在支持能耗感知編程方面的能力,利用它測(cè)量了幾個(gè)案例程序,分析編程模式對(duì)于能耗的影響,并對(duì)代碼進(jìn)行了優(yōu)化。在現(xiàn)代計(jì)算機(jī)中,數(shù)據(jù)的訪問(wèn)和移動(dòng)都會(huì)消耗一定的能量,因此實(shí)驗(yàn)中選擇了幾個(gè)與數(shù)據(jù)存放和訪問(wèn)模式相關(guān)的案例。實(shí)驗(yàn)對(duì)象程序選自Rodinia和Rogue Wave公司的白皮書(shū)。
實(shí)驗(yàn)平臺(tái)采用Intel Haswell-EP 架構(gòu)服務(wù)器,配置如表3 所示,服務(wù)器上有兩個(gè)Intel Xeon E5-2680 v3 處理器(2.50 GHz,12-core),安裝了Centos 操作系統(tǒng)(Linux 內(nèi)核版本是3.10.0)。Haswell-EP 架構(gòu)支持讀取PACKAGE 和DRAM 的RAPL 能耗信息。
表3 實(shí)驗(yàn)平臺(tái)配置Table 3 Experiment platform configuration
所有實(shí)驗(yàn)的案例程序均使用gcc 及-O3 選項(xiàng)進(jìn)行編譯,鏈接成可執(zhí)行文件,并使用likwid-pin 工具將程序指定在PACKAGE1 的同一個(gè)核上運(yùn)行。在運(yùn)行過(guò)程中,本文使用FPowerTool 工具收集程序在運(yùn)行過(guò)程中的性能事件和能耗信息。
由于程序的能耗和性能之間可能相互牽制,為了綜合評(píng)價(jià)優(yōu)化對(duì)二者的影響,本文選用能耗時(shí)延積(energy delay product,EDP)作為評(píng)價(jià)能耗與性能優(yōu)化技術(shù)好壞的綜合指標(biāo)。
EDP 的計(jì)算公式如式(3)所示,其中表示消耗的能量,表示運(yùn)行時(shí)間,的值為1、2 或3,在實(shí)際運(yùn)用時(shí)會(huì)對(duì)數(shù)據(jù)進(jìn)行規(guī)則化處理。
在能耗感知編程過(guò)程中,EDP 的值越小表示能耗效率越高,性能也越好。
第一個(gè)實(shí)驗(yàn)有關(guān)變量賦值冗余。變量賦值冗余是指在短時(shí)間內(nèi)對(duì)內(nèi)存的同一地址重復(fù)寫(xiě)入相同的值,也稱(chēng)為重復(fù)寫(xiě),重復(fù)寫(xiě)會(huì)造成程序能耗增加、性能下降。實(shí)驗(yàn)中選取了異構(gòu)計(jì)算的基準(zhǔn)測(cè)試程序集Rodinia中的LavaMD程序。LavaMD是一個(gè)OpenMP的程序,用來(lái)計(jì)算3D 空間中由于粒子之間的相互作用而導(dǎo)致的粒子電勢(shì)和粒子位置的重定位。
首先對(duì)程序進(jìn)行能耗測(cè)量,測(cè)量結(jié)果如圖5 所示。從能耗信息中,可以發(fā)現(xiàn)kernel_cpu 函數(shù)是程序的能耗熱點(diǎn)。
圖5 LavaMD 原生程序的能耗信息Fig.5 Energy consumption of original LavaMD program
利用動(dòng)態(tài)插樁對(duì)LavaMD 程序進(jìn)行重復(fù)寫(xiě)的檢測(cè)。對(duì)源代碼內(nèi)層for 循環(huán)的分析發(fā)現(xiàn),在對(duì)變量2的賦值計(jì)算中,不變,為for 循環(huán)的自變量,也就是說(shuō)2 的值依賴(lài)于變量[].的值,但是變量[].的相鄰元素存在大量的重復(fù)值。優(yōu)化方案是在對(duì)2賦值計(jì)算之前,添加一個(gè)對(duì)變量[].的值的判斷語(yǔ)句,如果變量[].值不變,就不再進(jìn)行后續(xù)計(jì)算和賦值。下面是相關(guān)的原生代碼和優(yōu)化后的代碼。
優(yōu)化前后的LavaMD 相關(guān)代碼:
優(yōu)化前后的kernel_cpu 函數(shù)的能耗和性能事件如表4 和表5 所示??梢钥闯?,優(yōu)化后的函數(shù)在處理器和DRAM 上的能耗都有所下降,而且?guī)缀跛行阅苁录臄?shù)量都有所下降,這正是其能耗下降的主要原因。在能耗下降的同時(shí),程序執(zhí)行時(shí)間也縮短了12%。
多余的變量定義在程序模塊復(fù)用中是常見(jiàn)的現(xiàn)象。有些模塊為了兼顧多種用途,變量定義覆蓋較廣,但是在某個(gè)特定應(yīng)用中,卻只需使用其中的一部分。這個(gè)實(shí)驗(yàn)給出多余變量定義影響能耗的一個(gè)例子。程序1 和程序2 分別是優(yōu)化前后的代碼片段。對(duì)比這兩個(gè)代碼片段,不同之處在于程序1 中的data結(jié)構(gòu)體的定義中存在沒(méi)有用到的成員c 和成員d,如下所示。
表4 kernel_cpu 函數(shù)優(yōu)化前后能耗變化Table 4 Energy consumed by kernel_cpu before and after optimization
表5 kernel_cpu 函數(shù)優(yōu)化前后性能事件對(duì)比Table 5 Performance events of kernel_cpu before and after optimization
程序1 和程序2:
FPowerTool 測(cè)得的能耗和程序運(yùn)行中發(fā)生的性能事件分別如表6 和表7 所示。
表6 程序1 和程序2 的能耗結(jié)果Table 6 Energy consumed by program 1 and program 2
表7 程序1 和程序2 的性能事件統(tǒng)計(jì)Table 7 Performance events statistics of program 1 and program 2
從測(cè)得的能耗和性能硬件事件結(jié)果可以看出,程序2 的執(zhí)行時(shí)間較短,能耗較低,所產(chǎn)生的cache 撲空相關(guān)的性能事件(如cache misses、L1dreadmiss 等)的數(shù)量都少得多,其能效及性能都優(yōu)于程序1。其原因是,現(xiàn)代處理器中的cache 以緩存行(cache line)形式緩存用戶(hù)的數(shù)據(jù),一個(gè)典型的緩存行通常為32 Byte或64 Byte,對(duì)應(yīng)內(nèi)存的一塊地址。在處理數(shù)據(jù)的過(guò)程中,CPU 會(huì)按緩存行來(lái)寫(xiě)入或讀取內(nèi)存的數(shù)據(jù)。程序1 中定義的數(shù)據(jù)會(huì)在內(nèi)存中連續(xù)存放,緩存行中存放了多個(gè)結(jié)構(gòu)體成員。但程序1 從內(nèi)存中讀取數(shù)據(jù)的過(guò)程中只用到了成員a 和成員b,緩存中的緩存行中只有一半的數(shù)據(jù)被訪問(wèn),成員c 和成員d 并不使用,這是多余變量定義的典型例子。
程序2 中數(shù)據(jù)在內(nèi)存緩存行中只有成員a 和成員b,從內(nèi)存讀取到緩存行的所有數(shù)據(jù)都被用到,沒(méi)有多余變量占據(jù)cache 空間。cache 空間利用更有效,緩存行的替換會(huì)減少。
圖6 顯示了程序1 和程序2 的執(zhí)行時(shí)間、PACKAGE1 能量和EDP 的對(duì)比,圖中的數(shù)據(jù)均經(jīng)過(guò)規(guī)則化處理。以程序1 的數(shù)據(jù)規(guī)則化為1 作為基準(zhǔn)。其中,EDP1=×,EDP2=×,EDP3=×??梢?jiàn),通過(guò)簡(jiǎn)單消除多余變量,就可以顯著改善程序的能效和性能。
圖6 程序1 和程序2 的執(zhí)行時(shí)間、能量和EDPFig.6 Normalized execution time,energy and EDP of program 1 and program 2
與案例2 類(lèi)似,程序3 和程序4 分別是優(yōu)化前和優(yōu)化后的代碼片段。兩段程序中的struct結(jié)構(gòu)體定義中都存在沒(méi)有用到的成員b 和成員c,但它們?cè)诮Y(jié)構(gòu)體中所處的位置不同,如下所示。
程序3 和程序4:
FPowerTool 測(cè)得的能耗和程序運(yùn)行中發(fā)生的性能事件分別如表8 和表9 所示??梢钥吹匠绦? 僅僅通過(guò)調(diào)整struct 變量定義中成員的位置,就能得到較好的能耗及性能,這是一個(gè)理解數(shù)據(jù)按字對(duì)齊存儲(chǔ)的典型例子。通常編譯器會(huì)根據(jù)數(shù)據(jù)字段的大小和類(lèi)型來(lái)決定它們?cè)趦?nèi)存中的存放方式。程序3 定義的結(jié)構(gòu)體成員a 在緩存行存放時(shí),因?yàn)閮?nèi)存需要按字節(jié)對(duì)齊而浪費(fèi)掉3 個(gè)字節(jié)空間。同樣,程序3 中結(jié)構(gòu)體成員c 也浪費(fèi)掉3 個(gè)字節(jié)空間。而程序4 定義的結(jié)構(gòu)體成員a 和成員c 在緩存行中按字節(jié)對(duì)齊時(shí),可以連續(xù)排列,只浪費(fèi)掉2 個(gè)字節(jié)空間。很明顯程序3 的cache 空間利用率要比程序4 的低很多,因此導(dǎo)致cache撲空的性能事件數(shù)量更多。
表8 程序3 和程序4 的能耗結(jié)果Table 8 Energy consumed by program 3 and program 4
圖7 顯示了程序3 和程序4 規(guī)則化后的執(zhí)行時(shí)間、PACKAGE1 能量和EDP 的對(duì)比,可以看出程序4 實(shí)現(xiàn)了更優(yōu)的能效和性能。這個(gè)案例說(shuō)明,與按字對(duì)齊存儲(chǔ)方式相匹配的數(shù)據(jù)放置可以改善程序能效和性能。
表9 程序3 和程序4 的性能事件統(tǒng)計(jì)情況Table 9 Performance events statistics of program 3 and program 4
圖7 程序3 和程序4 的執(zhí)行時(shí)間、能量和EDPFig.7 Normalized execution time,energy and EDP of program 3 and program 4
優(yōu)化前后的代碼片段程序5 和程序6 的不同之處在于這兩個(gè)代碼段對(duì)數(shù)組訪問(wèn)的模式不一樣。這是一個(gè)典型的數(shù)組按行還是按列訪問(wèn)對(duì)性能的影響問(wèn)題。所處理的數(shù)據(jù)構(gòu)成一個(gè)1 024 行(row)10 240列(col)的數(shù)組。數(shù)組在內(nèi)存中按行序存儲(chǔ),也就是說(shuō)同一行中的單元連續(xù)存放,存完一行再存下一行。Cache 從內(nèi)存每次加載一個(gè)緩存行。程序5 和程序6 都用和分別作為行和列的索引對(duì)數(shù)組進(jìn)行訪問(wèn),如下所示。
程序5 和程序6:
圖8 顯示了兩個(gè)程序的訪問(wèn)邏輯,虛線線頭顯示出緩存中數(shù)據(jù)的訪問(wèn)次序。程序5 是按列訪問(wèn),如圖8(a)所示,每加載一個(gè)緩存行,只訪問(wèn)對(duì)應(yīng)列中的一個(gè)單元,然后加載下一緩存行。而程序6 是按行訪問(wèn),每次加載一個(gè)新的緩存行,會(huì)連續(xù)訪問(wèn)該行的所有單元,其訪問(wèn)效果如圖8(b)所示。
圖8 按列訪問(wèn)與按行訪問(wèn)對(duì)比Fig.8 Comparison of per-column access and per-row access
FPowerTool 測(cè)得的能耗和程序運(yùn)行中發(fā)生的性能事件分別如表10 和表11 所示。
表10 程序5 和程序6 的能耗結(jié)果Table 10 Energy consumed by program 5 and program 6
從以上訪存模式的分析中可知,對(duì)特定存儲(chǔ)方式的數(shù)組的不同訪問(wèn)模式會(huì)導(dǎo)致不同的能耗及性能。程序6 的按行訪問(wèn)模式與按行連續(xù)存儲(chǔ)模式相匹配,使每次加載的cache 行中的數(shù)據(jù)得到充分利用,因此cache 空間利用率高,避免了從內(nèi)存的頻繁加載,執(zhí)行中與cache 撲空相關(guān)的事件數(shù)量明顯減少,不僅程序性能得到提高,其能效也得到改善,表10 和表11 中的能耗和性能事件數(shù)據(jù)印證了這一點(diǎn)。圖9 顯示了程序5 和程序6 規(guī)則化后的執(zhí)行時(shí)間、PACKAGE1 能量和EDP 的對(duì)比,程序6 明顯占優(yōu)。這個(gè)案例說(shuō)明,數(shù)組數(shù)據(jù)的訪問(wèn)模式一定要和數(shù)據(jù)的放置相匹配,按行連續(xù)存放與行序訪問(wèn)相匹配;反之,按列連續(xù)存放要求列序的訪問(wèn)。
表11 程序5 和程序6 的性能事件統(tǒng)計(jì)情況Table 11 Performance events statistics of program 5 and program 6
圖9 程序5 和程序6 的執(zhí)行時(shí)間、能量和EDPFig.9 Normalized execution time,energy and EDP of program 5 and program 6
從案例2~案例4 可知,數(shù)據(jù)放置和訪問(wèn)模式對(duì)程序的性能和能耗有很大影響,通過(guò)簡(jiǎn)單地消除多余變量,變換struct結(jié)構(gòu)體成員的位置,改變數(shù)組元素的訪問(wèn)次序,就可以顯著提高程序的執(zhí)行性能,降低程序的能耗??梢灶A(yù)見(jiàn),諸如循環(huán)展開(kāi)、數(shù)據(jù)預(yù)取、流水處理、通信計(jì)算重疊以及數(shù)據(jù)放置等方面的性能優(yōu)化手段也可能改善程序的能耗,但是需要用工具去測(cè)量,用實(shí)驗(yàn)去驗(yàn)證。
以上實(shí)驗(yàn)也顯示了FPowerTool 在能耗感知編程過(guò)程中的角色和使用方式。通過(guò)測(cè)量程序基本代碼段的能耗和性能事件,定位程序能耗熱點(diǎn),了解熱點(diǎn)代碼段中的性能事件水平,分析其對(duì)能耗的影響,然后有針對(duì)性地改進(jìn)程序中的數(shù)據(jù)分布、數(shù)據(jù)訪問(wèn)模式,使其盡可能與特定的硬件相匹配。這個(gè)優(yōu)化過(guò)程可以多次迭代,逐步逼近優(yōu)化的目標(biāo)。在這個(gè)過(guò)程中,F(xiàn)PowerTool 可起到獲取真實(shí)數(shù)據(jù)、定量評(píng)估優(yōu)化效果的作用。
本文提出了能耗感知編程的概念,針對(duì)能耗感知編程對(duì)能耗測(cè)量和分析的需求,提出了一種程序能耗與性能事件協(xié)同測(cè)量與分析的方法EPC。該方法通過(guò)采集處理器內(nèi)部的硬件計(jì)數(shù)器,獲得程序能耗以及執(zhí)行過(guò)程中的性能事件數(shù)值,定位能耗高的程序代碼段,并通過(guò)時(shí)間戳對(duì)能耗和性能事件進(jìn)行關(guān)聯(lián)分析,找出影響程序能耗的主要性能事件。由于該方法無(wú)需修改被測(cè)代碼,額外開(kāi)銷(xiāo)低,對(duì)被測(cè)程序的打擾少,測(cè)量結(jié)果失真較低。基于該方法實(shí)現(xiàn)了面向能耗感知編程的測(cè)量分析工具FPowerTool,使用FPowerTool 對(duì)幾個(gè)程序優(yōu)化案例進(jìn)行了評(píng)測(cè)和優(yōu)化,揭示了變量定義、賦值、放置和訪問(wèn)模式對(duì)程序能耗的影響。實(shí)驗(yàn)結(jié)果表明,改進(jìn)變量定義、賦值、放置與訪問(wèn)方式可以提高cache 利用率,提升程序性能,改善程序能效。這幾個(gè)實(shí)驗(yàn)案例初步驗(yàn)證了FPowerTool在支持能耗感知編程中的有效性。
FPowerTool 在一定程度上解決了能耗感知編程面臨的基本問(wèn)題,但是其使用仍具有一定的局限性。首先,能耗采樣的分辨率受限于處理器內(nèi)部硬件計(jì)數(shù)器的采樣頻率,它不能區(qū)分指令級(jí)別的能耗,只能做到代碼段級(jí)。在多數(shù)情況下,這對(duì)定位程序能耗熱點(diǎn)是足夠的。要追求更細(xì)粒度的指令級(jí)別的能耗測(cè)量則依賴(lài)于處理器內(nèi)部計(jì)數(shù)器采樣分辨率的提高。其次,能耗和性能事件關(guān)聯(lián)關(guān)系的分析目前自動(dòng)化水平還不高,更多依賴(lài)程序員的經(jīng)驗(yàn),需要發(fā)展更為智能的輔助分析工具。這些將是下一步的研究工作。