阿里云系統(tǒng)團(tuán)隊(duì),是由原淘寶內(nèi)核組擴(kuò)建而成,2013年淘寶內(nèi)核組響應(yīng)阿里巴巴集團(tuán)的號(hào)召,整建制轉(zhuǎn)入阿里云,開(kāi)始為云計(jì)算底層系統(tǒng)構(gòu)建完善的系統(tǒng)支持。 阿里云系統(tǒng)團(tuán)隊(duì)是由一群具有高度使命感和自我追求的內(nèi)核開(kāi)發(fā)人員組成,團(tuán)隊(duì)中的大多數(shù)人,都是活躍的社區(qū)內(nèi)核開(kāi)發(fā)人員。目前的工作領(lǐng)域主要涉及(但不限于) Linux內(nèi)核的內(nèi)存管理、文件系統(tǒng)、網(wǎng)絡(luò)和內(nèi)核維護(hù)構(gòu)建,以及和內(nèi)核相關(guān)聯(lián)的用戶態(tài)庫(kù)和工具。如果你對(duì)我們的工作很感興趣,歡迎加入我們,請(qǐng)將簡(jiǎn)歷發(fā)送至 tao.ma at linux.alibaba.com或者boyu.mt at alibaba-inc.com。
1. 什么是 CPI ?
本小節(jié)講述為什么使用 CPI 分析程序性能的意義。如果已經(jīng)非常了解 CPI 對(duì)分析程序性能的意義,可以跳過(guò)本小節(jié)的閱讀。
1.1 程序怎么樣才能跑得快 ?
理解什么是 CPI,首先讓我們思考一個(gè)問(wèn)題:在一個(gè)給定的處理器上,如何才能讓程序跑得更快呢?
假設(shè)程序跑得快慢的標(biāo)準(zhǔn)是程序的執(zhí)行時(shí)間,那么程序執(zhí)行的快慢,就可以用如下公式來(lái)表示:
因此,要想程序跑得快,即減少程序執(zhí)行時(shí)間,我們就需要在以下三個(gè)方面下功夫:
減少程序總指令數(shù)
要減少程序執(zhí)行的總指令數(shù),可能有以下手段:
算法優(yōu)化;好的算法設(shè)計(jì),可能帶來(lái)更少的指令執(zhí)行數(shù)。
更高效的編譯器或者解釋器;新的編譯器或者解釋器,可能對(duì)同樣的源代碼,生成更少的機(jī)器碼。
用更底層的語(yǔ)言優(yōu)化;這是為何 Linux 內(nèi)核代碼使用 C 語(yǔ)言,并且還喜歡內(nèi)聯(lián)匯編。
更新的處理器指令;新的處理器指令,對(duì)處理某類特殊目的運(yùn)算更有幫助,而新版本編譯器最重要的工作就是,在新的處理器上,用最新的高效指令;例如,x86 SSE,AVX 指令。
這一點(diǎn)很容易理解,縮短 CPU 時(shí)鐘周期的時(shí)間,實(shí)際上就是要提高 CPU 的主頻。這正是 Intel 過(guò)去戰(zhàn)無(wú)不勝的法寶之一。今天,由于主頻的提高已經(jīng)到了制造工藝的極限,CPU 時(shí)鐘周期的時(shí)間很難再繼續(xù)降低了。
減少每指令執(zhí)行所需平均時(shí)鐘周期數(shù)
如何減少每指令執(zhí)行所需平均 CPU 時(shí)鐘周期數(shù)呢?讓我們先從 CPU 設(shè)計(jì)角度看一下:
標(biāo)量處理器 (Scalar Processor) ;一個(gè) CPU 時(shí)鐘周期只能執(zhí)行一條指令;
超標(biāo)量處理器 (Superscalar Processor);一個(gè) CPU 時(shí)鐘周期可以執(zhí)行多條指令。
因此不難看出,如果使用支持超標(biāo)量處理器的 CPU,利用 CPU 流水線提高指令并行度,那么就可以達(dá)到我們的目的了。流水線的并行度越高,執(zhí)行效率越高,那么每指令執(zhí)行所需平均時(shí)鐘周期數(shù)就會(huì)越低。
當(dāng)然,流水線的并行度和效率,又取決于很多因素,例如,取指令速度,訪存速度,指令亂序執(zhí)行 (Out-Of-Order Execution),分支預(yù)測(cè)執(zhí)行 (Branch Prediction Execution),投機(jī)執(zhí)行 (Speculative Execution)的能力。一旦流水線并行執(zhí)行的能力降低,那么程序的性能就會(huì)受到影響。關(guān)于超標(biāo)量處理器,流水線,亂序執(zhí)行,投機(jī)執(zhí)行的細(xì)節(jié),這里不再一一贅述,請(qǐng)查閱相關(guān)資料。
另外,在 SMP,或者多核處理器系統(tǒng)里,程序還可以通過(guò)并行編程來(lái)提高指令的并行度,因此,這也是為什么今天在 CPU 主頻再難以提高的情況下,CPU 架構(gòu)轉(zhuǎn)為 Multi-Core 和 Many-Core。
由于提高 CPU 主頻的同時(shí),又要保障一個(gè) CPU 時(shí)鐘周期可以執(zhí)行更多的指令,因此處理器廠商需要不斷地提高制造工藝,降低 CPU 的芯片面積和功耗。
1.2 CPI 和 IPC
在計(jì)算機(jī)體系結(jié)構(gòu)領(lǐng)域,經(jīng)??梢钥吹?CPI 的使用。CPI 即 Cycle Per Instruction 的縮寫(xiě),它的含義就是每指令周期數(shù)。此外,在一些場(chǎng)合,也可以經(jīng)??吹?IPC,即 Instruction Per Cycle,含義為每周期指令數(shù)。
因此不難得出,CPI 和 IPC 的關(guān)系為,
使用 CPI 這個(gè)定義,本文開(kāi)篇用于衡量程序執(zhí)行性能的公式,如果具體到單 CPU 的程序執(zhí)行性能場(chǎng)景,實(shí)際上可以表示為:
由于受到硅材料和制造工藝的限制,處理器主頻的提高已經(jīng)面臨瓶頸,因此,程序性能的提高,主要的變量在 Instruction Count 和 CPI 這兩個(gè)方面。
在 Linux 上,通過(guò)perf工具,通過(guò) Intel 處理器提供的寄存器 (PMU),可以很容易測(cè)量一個(gè)程序的 IPC。例如,下例就可以給出 Java 程序的 IPC,8 秒多的時(shí)間里,這個(gè) Java 程序的 IPC 是 0.54:
那么,通過(guò) IPC,我們也可以換算出 CPI 是1/0.54,約為 1.85.
通常情況下,通過(guò) CPI 的取值,我們可以大致判斷一個(gè)計(jì)算密集型任務(wù),到底是 CPU 密集型的還是 Memory 密集型的:
CPI 小于 1,程序通常是 CPU 密集型的;
CPI 大于 1,程序通常是 Memory 密集型的;
1.3 重新認(rèn)識(shí) CPU 利用率
對(duì)程序員來(lái)說(shuō),判斷一個(gè)計(jì)算密集型任務(wù)運(yùn)行效率的重要依據(jù)就是看程序運(yùn)行時(shí)的 CPU 利用率。很多人認(rèn)為 CPU 利用率高就是程序的代碼在瘋狂運(yùn)行。實(shí)際上,CPU 利用率高,也有可能是 CPU 正在忙等一些資源,如訪問(wèn)內(nèi)存遇到了瓶頸。
一些計(jì)算密集型任務(wù),在正常情況下,CPI 很低,性能原本很好。CPU 利用率很高。但是隨著系統(tǒng)負(fù)載的增加,其它任務(wù)對(duì)系統(tǒng)資源的爭(zhēng)搶,導(dǎo)致這些計(jì)算任務(wù)的 CPI 大幅上升,性能下降。而此時(shí),很可能 CPU 利用率上看,還是很高的,但是這種 CPU 利用率的高,實(shí)際上體現(xiàn)的是 CPU 的忙等,及流水線的停頓帶來(lái)的效應(yīng)。
Brendan Gregg 曾在CPU Utilization is Wrong這篇博客中指出,CPU 利用率指標(biāo)需要結(jié)合 CPI/IPC 指標(biāo)一起來(lái)分析。并詳細(xì)介紹了前因后果。感興趣的讀者可以自行閱讀原文,或者訂閱內(nèi)核月談公眾號(hào),閱讀我們公眾號(hào)非??孔V的譯文。
至此,相信讀者已經(jīng)清楚,在不修改二進(jìn)制程序的前提下,通過(guò) CPI 指標(biāo)了解程序的運(yùn)行性能,有著非常重要的意義。對(duì)于計(jì)算密集型的程序,只通過(guò) CPU 利用率這樣的傳統(tǒng)指標(biāo),也無(wú)法幫助你確認(rèn)你的程序的運(yùn)行效率,必須將 CPU 利用率和 CPI/IPC 結(jié)合起來(lái)看,確定程序的執(zhí)行效率。
1.4 如何分析 CPI/IPC 指標(biāo)異常?
雖然利用perf可以很方便獲取 CPI/IPC 指標(biāo),但是想分析和優(yōu)化程序高 CPI 的問(wèn)題,就需要一些工具和分析方法,將 CPI 高的原因,以及與之關(guān)聯(lián)的軟件的調(diào)用棧找到,從而決定優(yōu)化方向。
關(guān)于 CPI 高的原因分析,在 Intel 64 and IA-32 Architectures Optimization Reference Manual, 附錄 B 里有介紹。其中主要的思路就是按照自頂向下的方法,自頂向下排查, 4 種引起 CPI 變高的主要原因,由于本文主要是介紹 CPI 火焰圖,
對(duì)于本小節(jié)的自頂向下的分析方法,限于篇幅所限,就不詳細(xì)展開(kāi)了,我們稍后會(huì)有專門的文章做詳細(xì)介紹。
2. CPI 火焰圖
Brendan Gregg 在CPI Flame Graphs: Catching Your CPUs Napping一文中,介紹了使用 CPI 火焰圖來(lái)建立 CPI 和軟件調(diào)用棧的關(guān)聯(lián)。
我們已經(jīng)知道,光看 CPU 利用率并不能知道 CPU 在干嘛。因?yàn)?CPU 可能執(zhí)行到一條指令就停下來(lái),等待資源了。這種等待對(duì)軟件是透明的,因此從用戶角度看,CPU 還是在被使用狀態(tài),但是實(shí)際上,指令并沒(méi)有有效地執(zhí)行,CPU 在忙等,這種 CPU 利用率并不是有效的利用率。
要發(fā)現(xiàn) CPU 在 busy 的時(shí)候?qū)嶋H上在干什么,最簡(jiǎn)單的方法就是測(cè)量平均 CPI。CPI 高說(shuō)明運(yùn)行每條指令用了更多的周期。這些多出來(lái)的周期里面,通常是由于流水線的停頓周期 (Stalled Cycles) 造成的,例如,等待內(nèi)存讀寫(xiě)。
而 CPI 火焰圖,可以基于 CPU 火焰圖,提供一個(gè)可視化的基于 CPU 利用率和 CPI 指標(biāo),綜合分析程序 CPU 執(zhí)行效率的方案。
下面這個(gè) CPI 火焰圖引用自 Brendan Gregg 博客文章。
可以看到,CPI 火焰圖是基于 CPU 火焰圖,根據(jù) CPI 的大小,在每個(gè)條加上了顏色。紅色代表指令,藍(lán)色代表流水線的停頓:火焰圖中,每個(gè)函數(shù)幀的寬度,顯示了函數(shù)或其子函數(shù)在 CPU 上的次數(shù),和普通 CPU 火焰圖完全一樣。而顏色則顯示了函數(shù)在 CPU 上是運(yùn)行 (running 紅色) 還是停頓 (stalled 藍(lán)色)。
火焰圖里,顏色范圍,從最高CPI為藍(lán)色(執(zhí)行最慢的指令),到最低CPI為紅色 (執(zhí)行最快的指令)?;鹧鎴D是 SVG 格式,矢量圖,因此支持鼠標(biāo)點(diǎn)擊縮放。
然而,Brendan Gregg 博客中的這篇博客,CPI 火焰圖是基于 FreeBSD 操作系統(tǒng)特有的命令生成的,而在 Linux 上,應(yīng)該怎么辦呢?
3. 一個(gè)小程序
讓我們寫(xiě)一個(gè)人造的小程序,展示在 Linux 下 CPI 火焰圖的使用。
這是一個(gè)最簡(jiǎn)的小程序,其中包含如下兩個(gè)函數(shù):
cpu_bound函數(shù)主體是 nop 指令的循環(huán);由于 nop 指令是不訪問(wèn)內(nèi)存的最簡(jiǎn)指令之一,因此該函數(shù) CPI 一定小于 1,屬于典型的 CPU 密集型的代碼。
memory_bound函數(shù)使用_mm_clflush驅(qū)逐緩存,人為觸發(fā)程序的 L1 D-Cache Load Miss。因此該函數(shù) CPI 必然大于 1,屬于典型的 Memory 密集型的代碼。
下面是程序的源碼:
在上述小程序運(yùn)行時(shí),我們使用如下命令生成 CPI 火焰圖,
最后生成的火焰圖如下,
可以看到,CPI 火焰圖看到的結(jié)果,是符合我們的預(yù)期的:
該程序所有的 CPU 時(shí)間,都分布在cpu_bound和memory_bound兩個(gè)函數(shù)里
同是 CPU 占用時(shí)間,但cpu_bound是紅色的,代表這個(gè)函數(shù)的指令在 CPU 上一直持續(xù)運(yùn)行
而memory_bound是藍(lán)色的,代表這個(gè)函數(shù)發(fā)生了嚴(yán)重的訪問(wèn)內(nèi)存的延遲,導(dǎo)致了流水線停頓,屬于忙等
4. 一個(gè)benchmark
現(xiàn)在,我們可以使用 CPI 火焰圖來(lái)分析一個(gè)略真實(shí)一些的測(cè)試場(chǎng)景。下面的 CPI 火焰圖,來(lái)自fio的測(cè)試場(chǎng)景。
這個(gè)fio對(duì) SATA 磁盤(pán),做多進(jìn)程同步 Direct IO 順序?qū)?,可以看到?/p>
紅顏色為標(biāo)記為 CPU Bound 的函數(shù)。其中顏色最深的是_raw_spin_lock,這是自旋鎖的等待循環(huán)引起的。
藍(lán)顏色為標(biāo)記為 Memory Bound 的函數(shù)。其中顏色最深的是fio測(cè)試程序的函數(shù)get_io_u,如果使用perf程序進(jìn)一步分析,這個(gè)函數(shù)里發(fā)生了嚴(yán)重的 LLC Cache Miss。
因?yàn)?CPI 火焰圖是矢量圖,支持縮放,所以以上結(jié)論可以通過(guò)放大get_io_u的調(diào)用棧進(jìn)一步確認(rèn),
到這里,讀者會(huì)發(fā)現(xiàn),使用 CPI 火焰圖,可以很方便地做 CPU 利用率的分析,找到和定位引發(fā) CPU 停頓的函數(shù)。一旦找到相關(guān)的函數(shù),就可以通過(guò)perf annotate命令對(duì)引起停頓的指令作出進(jìn)一步確認(rèn)。并且,我們可以利用1.4小節(jié)的自頂向下分析方法,對(duì) CPU 哪個(gè)環(huán)節(jié)產(chǎn)生瓶頸作出判斷。最后,結(jié)合這些信息,決定優(yōu)化方向。
5. 小結(jié)
本文介紹了使用 CPI 火焰圖分析程序性能的方法。CPI 火焰圖不但展示了程序的 Call Stack 與 CPU 占用率的關(guān)聯(lián)性,而且還揭示了這些 CPU 占用率里,哪些部分是真正的有效的運(yùn)行時(shí)間,哪些部分實(shí)際上是 CPU 因某些停頓造成的忙等。
系統(tǒng)管理員可以通過(guò)此工具發(fā)現(xiàn)系統(tǒng)存在的資源瓶頸,并且通過(guò)一些系統(tǒng)管理命令來(lái)緩解資源的瓶頸;例如,應(yīng)用間的 Cache 顛簸干擾,可以通過(guò)將應(yīng)用綁到不同的 CPU 上解決。
而應(yīng)用開(kāi)發(fā)者則可以通過(guò)優(yōu)化相關(guān)函數(shù),來(lái)提高程序的性能。例如,通過(guò)優(yōu)化代碼減少 Cache Miss,從而降低應(yīng)用的 CPI 來(lái)減少處理器因訪存停頓造成的性能問(wèn)題。
"Linux閱碼場(chǎng)"是專業(yè)的Linux及系統(tǒng)軟件技術(shù)交流社區(qū),企業(yè)和Linux人才的連接樞紐。
- 
                                Linux
                                +關(guān)注
關(guān)注
88文章
11586瀏覽量
217347 - 
                                程序
                                +關(guān)注
關(guān)注
117文章
3833瀏覽量
84605 
原文標(biāo)題:用CPI火焰圖分析Linux性能問(wèn)題
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Arm高性能計(jì)算工具試用分析
什么是性能分析
快速識(shí)別應(yīng)用程序性能瓶頸
DVFS對(duì)程序性能影響模型
7個(gè)好習(xí)慣快速提升Python程序性能
    
了解 LabVIEW 程序運(yùn)行性能的關(guān)鍵因素
    
利用矢量硬件如何提高應(yīng)用程序性能
針對(duì)ADAS應(yīng)用優(yōu)化編譯程序性能分析
LabVIEW開(kāi)發(fā)教程之運(yùn)行性能的提升技巧詳細(xì)說(shuō)明
    
如何使用perf和vtune進(jìn)行性能分析
LabVIEW應(yīng)用程序中性能瓶頸的解決
    
通過(guò)32Gb/S光纖通道提高應(yīng)用程序性能
    
第6代光纖通道:加速全閃存數(shù)據(jù)中心的數(shù)據(jù)訪問(wèn)和應(yīng)用程序性能
    
使用Brocade Gen 7 SAN確保應(yīng)用程序性能和可靠性
    
          
        
        
了解CPI對(duì)分析程序性能的意義
                
 
           
            
            
                
            
評(píng)論