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