今天的東西可能會比較輕松哈,不會特別不會特別細(xì)。給大家講一下,因?yàn)楝F(xiàn)在硬件按理論來說硬件會越來越好,為什么還要去做這么深的投入和這么多精力去做代碼性能調(diào)優(yōu)。然后再舉一個例子,怎么去做性能分析?基于 Top-down 性能分析,以及怎么優(yōu)化它?
02 背景:世界日益數(shù)據(jù)化,數(shù)據(jù)處理的性能要求愈加強(qiáng)烈
首先看,現(xiàn)在整個世界基本上是以數(shù)據(jù)為中心,數(shù)據(jù)越來越多,對數(shù)據(jù)處理的性能要求會越來越強(qiáng)烈。從現(xiàn)在可以看到,整個咱們整個全世界出現(xiàn)的數(shù)據(jù)大概是從 2020 年已經(jīng)到了 40CB 。因?yàn)樗侵笖?shù)性的增長,今年肯定還會更多。
03 背景:處理器單線程處理性能增長進(jìn)入瓶頸期
從硬件上,摩爾定律其實(shí)已經(jīng)進(jìn)到一個尾聲階段了。大家可以看到圖上這一塊是晶體管的數(shù)量是在慢慢增加的。頻率可以看到大家基本上也是慢慢的進(jìn)入一個持平的狀態(tài)。比如現(xiàn)在手機(jī)上高通可能每年提就擠牙膏似的,跟 Intel 一樣擠牙膏擠一點(diǎn)點(diǎn)。功耗它可能現(xiàn)在可能在手機(jī)上,功耗會越來越高,手機(jī)現(xiàn)在越來越發(fā)燙。
但是最關(guān)鍵是單線程性能,我可能核會越來越多,但是單線程性能并沒有太增加。我可能會通過手機(jī)是小核,中核,大核這樣一個策略來給它性能提升,但是對單核來說,它性能并沒有特別明顯的提升,這是它的一個背景。
04 趨勢:未來計算性能優(yōu)化的三個維度
這個人叫 John Hennessy,他是哈佛大學(xué)的前校長,現(xiàn)在應(yīng)該和 alphabet (谷歌母公司) 的董事長。他當(dāng)時做了一個演講,計算的未來是什么樣。他說對于通用的計算,其實(shí)現(xiàn)在來說已經(jīng) "is dead" ,已經(jīng)死了。
接下來應(yīng)該是什么樣子?下一對這篇論文就是,應(yīng)該是 20 年的一篇論文。他講將來我計算的一個提升有三個維度,第一個維度就是軟件,第二個維度就是算法,第三個維度就是硬件。
我先講講硬件,針對硬件的一個精簡,或者硬件的專業(yè)化,比如處理器更精簡,或者基于一個特定的算法,比如 GPU 或者 DSP 的硬件加速器。第二個就是新的一個算法,新的一些算法或者一些機(jī)器模型來去推算。
今天咱們的或者這本書,它能解決的問題是什么?可能是通過把我們的軟件工程,軟件性能工程領(lǐng)域,它里邊有兩個點(diǎn)。一個就是把軟件更精簡,再一個就是把軟件裁剪的更適合于硬件特性。今天其實(shí)這篇這本書里面講的更多的是這塊(軟件裁剪),他并沒有說我是怎么去改硬件,比如是怎么去設(shè)計一個芯片。我怎么基于硬件特征讓把相同的程序跑得更好?
在后摩爾時期,讓你代碼跑得更快,會越來越重要。尤其是把它裁剪的或者調(diào)優(yōu)的,更適合于它所運(yùn)行的硬件是很關(guān)鍵的。
05 機(jī)會在哪里?軟件的不同實(shí)現(xiàn)對性能表現(xiàn)上的差異
機(jī)會在哪里?這個地方它講了一下,同一個程序我用 Python 去寫,用 Java 去寫,用 C 去寫。普通的寫法,它的性能以 Python 為基準(zhǔn),比如 Python 是 1, Java 可能就是11,一 C 就是 47 ,如果是把程序簡單做一個強(qiáng)行的并行化是 366,到最終他用硬件本身的一個硬件特性,它有 6萬倍(相對于 Python )的增加。這個時間差距還是很大的,如果能把充分的把硬件給利用起來,更充分的去適應(yīng)具體的芯片,它的能力會提出很多。
06 軟件和硬件在性能方面均有能力范圍
其實(shí)我們一般都想去依賴于硬件的提升,CPU 提升去提升整體的性能。但是 CPU 它其實(shí)是一個死的,它并不說你給我隨便也給我一個指令,我都能把你運(yùn)行。不一定的。對編譯器來說,比如 GCC/LLVM 它是利用它的成本或者一個成本收益的模型去估算。但是如果他認(rèn)為你可能有個優(yōu)化的或者一個轉(zhuǎn)換,它認(rèn)為他可能風(fēng)險比較大的,或者會導(dǎo)致他損耗比較高,或什么時候他就不去做了。他會比較保守,可能就達(dá)不到默認(rèn)的優(yōu)化的預(yù)期。
07 現(xiàn)在是做軟件優(yōu)化的黃金時期
所以說現(xiàn)在可能就是最好的一個時機(jī)去做軟件優(yōu)化,前段時間也有人提過一個黃金十年就是這樣一個意思。如果對 Google 搜索增加 2% 的一個速度,可能它的搜索量就會有 2% 的增加。雅虎之前統(tǒng)計過,如果它一個網(wǎng)頁加載時間如果增加四百毫秒,可能使用率可能會降5% 到 9%。
但是整個東西來做,并不是你直接就可以拿到了,需要你深入的去理解你的軟件,還有你的硬件怎么去匹配,但是大家有沒有做好準(zhǔn)備呢?。
08 需要我們怎么做?構(gòu)建自己的技術(shù)棧
這時候需要我們怎么做?可能我們需要構(gòu)建自己的技術(shù)棧。技術(shù)棧有哪些呢?就有這些,跟手機(jī)一樣,手機(jī)應(yīng)用,還有它框架,編譯器,OS,還有硬件??赡軐υ蹅冞@本書里面涉及到的,主要的是這些。
?第一個就是編譯器,你可能不需要更深入理解編譯器的具體的原理,但是你要了解編譯器通用的編譯優(yōu)化手段,以及它有比較通用的一些編譯優(yōu)化的選項(xiàng)。
?第二個就是 OS 的一個調(diào)度,還有一個可能 CPU 綁核。在手機(jī)上的話,綁核還是很明顯的,如果是在小核上和大核,要是中核,它們也差距很多。硬件的限制。如果你想你的任務(wù)要跑特別快,比如假如一個特別重要的前臺功能,你需要把你的主線程一個界面相關(guān)的線程可能就要綁定到大核上,讓他跑這么快。
?第三個在硬點(diǎn)上,我可能我們要比較了解 CPU 的微架構(gòu)是什么樣的, CPU 微架構(gòu)什么樣的,為什么我的代碼跑的時候它就慢,慢又拆解為幾類,怎么去分析它。第二個你要去可能要去嘗試的去學(xué)習(xí),怎么去讀或者改這些匯編的一些指令。
09 避免性能調(diào)優(yōu)的誤區(qū)
接下來我們在性能調(diào)優(yōu)的時候會有些誤區(qū),這些誤區(qū)可能有這幾類。
?第一個就是我看這代碼,我自己猜哪個地方可能會有問題,就去改了,結(jié)果可能做了一些修改,其實(shí)對系統(tǒng)沒有任何影響,只是自己在瞎猜。我可能就是猜,但是我并不是知道我具體的數(shù)據(jù)什么樣的,并沒有真實(shí)去測它,這是很恐怖的。從我們公司內(nèi)部來看,我們搞問題的肯定是以終為始,一定要是拿到你具體對用戶體驗(yàn)相關(guān)的一個具體的問題,它是什么樣的,拿的數(shù)據(jù)來,從數(shù)據(jù)出發(fā)。
?第二個,可能你自己造了比較糟糕的一個 benchmark 和基準(zhǔn)測試,也是非常糟糕。最好是拿比較開源的,大家共同認(rèn)可的一個。
?最后一個,因?yàn)槎际菍W(xué)計算機(jī)相關(guān)專業(yè)的哈,可能大家比較信仰大O的這種算法的復(fù)雜度。但是其實(shí)在 CPU 上跑的時候,它并不一定說 O 你算法復(fù)雜度越低,它跑得越快,這不一定。
10 定位性能問題的方法
這本書其實(shí)講了這幾種方法。
?第一個方法扣的這樣的一個插樁,寫代碼打日志,我看到我函數(shù)執(zhí)行特別多,不是前后加日志。
?第二個生成最簡單火山圖,大家應(yīng)用也比較多了。
?第三個如果不搞編譯器可能大家用的比較少。 compare opt reports 就是編譯器的一個優(yōu)化的報告,你可以打開一個編譯開關(guān),你在編譯的時候把它報告拿出來。這個地方確實(shí)我用過一次,但我們是在用過幾次,在我們我們公司一個比較關(guān)鍵的特性里邊,他就報告里面,我發(fā)現(xiàn)有個地方,他可能有一個函數(shù)的內(nèi)聯(lián)沒有做,沒做其實(shí)它性能就比較差,我們做完以后性能有百分之將近 10% 的收益。這是很明顯的。
?再一個就是 TMA Top-down,也是書上的例子,待會展開。主要是基于 CPU 架構(gòu),應(yīng)該一個英特爾的,一個以色列的專家提出的構(gòu)想,對性能的分析來說是非常好。
?最后一個是 Roofline 五點(diǎn)線這個模型,CPU 里邊性能其實(shí)就涉及兩類,一個是計算,一個是訪存。一個性能與緩存,通過Roofline 線的來判斷,我的當(dāng)前的程序是在哪個地方。還有再一個就是我硬件當(dāng)前是什么樣一個情況,通過這兩個信息我就知道程序有沒有充分運(yùn)用硬件的能力,它的訪存,它計算都有沒有運(yùn)用全。所以這個地方是可以用來進(jìn)行反向的去優(yōu)化我的硬件設(shè)計,也可以來指導(dǎo)我的程序里面怎么去改進(jìn),以及是訪存的類型,我可能就要去優(yōu)化訪存的一個瓶頸,包括計算,我可能要從計算去交流,但這個目前好像沒有看到特別好的工具。作者也并沒有講,只是有這樣一個方法。我看到香山開源的 RISC-V 的芯片里面,其實(shí)他們相關(guān)的研究里面應(yīng)該也用到了這種方法。
11 性能優(yōu)化的技術(shù)點(diǎn)
剛才分析完了,我具體評定以后可能有哪些的優(yōu)化的方法。
?PGO(Profile GuidedOptimization),其實(shí)在手機(jī)上,在每個任何一個安卓手機(jī)上,它其實(shí)都在用。用戶用的時候它會產(chǎn)生profile,在虛擬機(jī)里面的產(chǎn)生 profile 采集用戶用的這些函數(shù)的次數(shù),它會都會收集起來。例如,等到晚上手機(jī)充電的時候,你如果關(guān)注晚上充電的時候就摸一下,可能比較燙,一個原因是充電的燙就可能后臺做相關(guān)的一些任務(wù),就是要基于 profile 進(jìn)行函數(shù)的一個指導(dǎo)編譯,因?yàn)樗幙赡芤粋€是比較大,再一個可能就比較激進(jìn)?;趐rofile 的調(diào)用關(guān)系,可能更多的可能會把一些函數(shù)依賴,或者把一些函數(shù)之間的距離可能給它重新排一下,這樣它調(diào)進(jìn)的時候,它 attach miss 就會比較小,會好一點(diǎn)。
?Vectorization 是向量化。
?Memory prefetching,后邊會講一個 memory prefetching 的例子。
?Optimize code layout,把你的代碼的樣子優(yōu)化一下。
?Function inlining,函數(shù)內(nèi)聯(lián)其實(shí)是用的挺多的,包括安卓它本身其實(shí)在虛擬機(jī)的修改,它在 AndroidT版本的修改,很多都是把函數(shù)進(jìn)行 inline,再進(jìn)行優(yōu)化的。剛才提到我們內(nèi)部也用了,針對一些個別場景,也會把一些函數(shù),尤其for循環(huán),比如在某一個函數(shù)的時候,它都會有調(diào)用慣例,這樣它會要壓棧出棧,這樣去還要保存寄存器里的信息,就會很損耗,把它去掉會好很多。
?Optimize memory accesses,在一些內(nèi)存的訪問的時候,比如讓它對齊。
?還有一些循環(huán)展開,再利用一些技術(shù)消除一些分支的預(yù)測的信息。
12 實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)
提到了一些方法,在書里面其實(shí)提到了一個是 perf。其實(shí)這還有一個叫 pmu-tools, Vtune 的一個開源版,Vtune 是一個 Intel 開發(fā)的一個工具,專門用于 Intel 的 PMU 架構(gòu)。 pmu-tools 是開源的一個工具。
再補(bǔ)充一下。今天材料主要就是原作者丹尼斯,他在 21 年對他寫的當(dāng)時做的一個材料,主要是也介紹梳理一些內(nèi)容。我的要聞把材料稍微改了一下,稍微補(bǔ)充一些信息。
13 Top-down 微體系結(jié)構(gòu)分析方法
這個給大家展示個例子哈。
首先我們看一下主要目的,給大家簡單的介紹一下什么是 Top-down。首先我先講一下 CPU 流水線。 CPU 流水線它其實(shí)有這幾個階段,一個指令都它執(zhí)行,是怎么執(zhí)行?它有一個取指-譯指-執(zhí)行-保存-回寫,其中前兩個階段是前端,主要是指令相關(guān)的,后三個階段是后端,主要是訪存相關(guān)的。如果其中有一個阻塞了,可能會導(dǎo)致空轉(zhuǎn),它的英文叫bound,比如 front and bound 和 back and bound, front and bound 可能是我取值的時候時間長。取指時間原因可能有多種,下面分這幾類。
指令一個是從這里面可能開始取過來,再進(jìn)行分發(fā),再給decode,再往下是執(zhí)行后端了。執(zhí)行的時候他可能不同的單元,我們可能要去不同的port,這個 port 里面給他去執(zhí)行。前后端是這樣串起來的。
到于指令執(zhí)行的時候,我們可以基于它 store 的在哪個階段給它進(jìn)行一個分類。第一層分了 4 類。
?第一個就是 Retiring ,你就退休了,你壽終就寢就行了。指令已經(jīng)執(zhí)行到五個階段,我都已經(jīng)執(zhí)行完了。OK,這是最好的一種狀態(tài)。如果所有的都是這塊,基本上相當(dāng)于就已經(jīng)自由了。完全利用了CPU 的整個流水線是非常好,但是基本上是不可能的,基本上不可能。
?第二個就是 Bad Speculation,錯誤的投機(jī)。有時候它可能會去提前執(zhí)行一些指令,比如有個 if-else,我們可能我不知道它是指哪一個,可能要投機(jī)去執(zhí)行了,我先執(zhí)行,我執(zhí)行完以后,我先不提交,提交給寄存器處理。這地方會給他進(jìn)行一個判斷,等到你真正的判斷執(zhí)行完了,如果你是對的,OK,我這部分信息我就提交過去,這邊就執(zhí)行完了。如果不對,我覺得你這個特性,我就把它都給它清掉,CPU 就在這里邊執(zhí)行的流水線執(zhí)行,相當(dāng)于這段時間這份資源就浪費(fèi)了,這一部分也會造成一定損失,這個也是希望它小的。
?第三個就是 Frontend Bound,剛提到這個取指的時候可能會慢。
?第四個叫 Backend Bound,一般比較多的。
然后再往下一層。
?Retiring 里面,可能還有一特殊的,到時候看書的可能也會提到,一般浮點(diǎn)可能會有問題。
?Bad Speculation 一般也他們很難去優(yōu)化它,可能也會有些困難。
?Frontend Bound 主要是兩類,取指令它有兩種,一個是延遲,一種是帶寬。帶寬大家可以看到帶寬過來,因?yàn)閹捥貏e窄,我可能取特別多。帶寬特別窄的時候我可能就取不過來了,取不過來我就等帶寬;延遲的話,我可能我取個指令的時候,可能我代碼編譯的可能特別大,比如他是剛執(zhí)行的 A ,執(zhí)行 B 并沒有在這里挨著,可能特別遠(yuǎn)。這時候我可能要去從我的內(nèi)存或者那地方再把它倒過來,那時候就會遇到 iTLB Miss 或者 i-Cache Miss,這時候就會導(dǎo)致等了時間會比較長。
?Backend Bound 分兩類,一個是 Core Bound ,一個是 Memory Bound 。 Core Bound大家簡單理解,計算類的,比如我可能我計算特別多,假如加法特別多,或者除法特別多的時候,這時候如果應(yīng)付不過來,這時候就會產(chǎn)生 Core Bound ,這地方可能會獲得時間比較長;最后一個就是 Memory Bound,這可能大家用的會相對可能會多一點(diǎn)。但是 Memory Bound 并不是直接指的內(nèi)存。里邊分好幾層,一個是L1/L2/L3 ,還有一個 DRAM Bound ,可能執(zhí)行過程中我需要讀數(shù)據(jù),從讀數(shù)據(jù)的時候接著開始讀數(shù)據(jù),如果在開始, L1 開始有了,OK,那就沒問題。最快的 L1 開始沒有,隨 L2 就會增加一些損耗。最好。如果 L2 沒有,隨 L3 再增加一些損耗。如果 L3 也沒有,那就得去 DRAM 里邊去取,時間會更長。在下面的涉及到 memory 這些帶寬,更大的 latency 。
OK,這就是 Top-down 的一個拆解?;诓鸾庖院?,你很明確的可以知道你的程序在 CPU 運(yùn)行的時候,到底依賴是哪個資源。你回過頭來,在不能改變硬件情況下,你怎么去改你的程序,把它更好的利用 CPU 的流水線或者它微架構(gòu),讓它運(yùn)行更快。
14 TMA分析實(shí)例 - 源碼
這個實(shí)例也比較簡單,我 malloc 一塊內(nèi)存,這內(nèi)存是 200 M 的,每個里面都寫個 0。我要干什么?我要寫一個這么大的循環(huán),應(yīng)該是一億次的一個循環(huán),每個循環(huán)我就隨機(jī)的訪問這一刻從 0 到 200M 內(nèi)存。訪問內(nèi)存,它運(yùn)行這個時間一般是 8. 5 秒。
怎么去通過用 Top-down 這個方法來進(jìn)行分析。其實(shí)補(bǔ)充一下,在 Linux 上,它英特爾應(yīng)該都是支持 perf,里面它有個 Top-down 的一個選項(xiàng),你可以試一下。它可以統(tǒng)計出來一層的我到底是哪種類型的 Bound,這里邊用的是 pmu-tools 這個方法。
15 TMA分析實(shí)例 - 1和2層分布
你可以看出來,它程序運(yùn)行的過程中,它 53% 是在 Backend Bound ,我剛才已經(jīng)看到 Backend Bound 是在后邊的一個。再看第二層,就是 L2 ,在 L2 可以看到,在這里面是 Memory Bound,對 Core Bound 是只有 8.80%,也不多。
16 TMA分析實(shí)例 - 3層分布
再看第三個 L3,L3 的 DRAM Bound,圖里邊右下角 Bound 時間,整個時間是整個占比47%。你可以看到里面每個 cycle 的時間,如 register 基本上一個 cycle 就搞定了,L1 就可能是 8 ~ 32 cycles ,L2 是 300 cycles ,已經(jīng)到 memory 了。memory 很多,你越低速度越慢時間越長。
17 TMA分析實(shí)例 - 通過perf定位到具體函數(shù)
然后怎么去定位它?有一個地方有對應(yīng)關(guān)系,這個工具理論是英特爾提出來的。英特爾有一個叫TMA metrics,大家可以在書上可以看到它有對應(yīng)表格,剛才可以看到它是 DRAM Bound 類型的,它的也需要會給你提供另外一個 L3 miss 的這樣一個 PMU 的事件讓你去用,你可以去統(tǒng)計 L3 Miss 以后,它就會到 DRAM 那里面去 L3 Miss 事件哪個最多,它通過 perf record 完以后,跟通過他去??梢酝ㄟ^解析審核的 data 文件, prop data 可以看到,這里面主要是復(fù)制函數(shù),里面他用到的是 move 的一個緩存的動作,100% 都在這個地方。
18 TMA分析實(shí)例 - 通過__buildin_prefetch內(nèi)存預(yù)取優(yōu)化
怎么去優(yōu)化它?在 for 循環(huán)里邊它不是有一個,剛才代碼里面可以看到它是隨機(jī)的去訪問它里邊的一個地址。
OK,他現(xiàn)在預(yù)計他提前能預(yù)取出來。它有三個參數(shù),第一個參數(shù)就是我預(yù)取的它的地址是哪一塊,把它預(yù)取到 cache 里邊來。第二個參數(shù) 0 代表是相當(dāng)于我是讀的,第三個參數(shù) 1 代表它的程度。第三個參數(shù)如果是 0 檔基本上我是本地,不是本地的我不取。我認(rèn)為這塊取了以后可能用的不特別多。 1 檔代表比較低程度, 2 檔代表比較中度的, 3 檔代表是高度的。
現(xiàn)在用__buildin_prefetch ,我理解應(yīng)該是可以緩解 L3 Miss 這一部分。改完以后效果,這個事件原來應(yīng)該是比原來相對小了 10 倍。大家可以看一下整個運(yùn)行性能,提升了 2 秒,原來是 8. 5,基本上也是 30% 的加速這樣一個模型。
19 性能分析調(diào)優(yōu)建議
最后丹尼斯在他書上也反復(fù)提了他的優(yōu)化建議。最后其實(shí)他有很多,挑出來這一部分翻譯成中文。大家一起來看一下。
?第一個默認(rèn)情況下軟件,它并不會到達(dá)你最優(yōu)的一個性能,寫完代碼以后,你認(rèn)為可能是有,但是它并不一定非常契合你的硬件,可能你需要還要針對硬件做一些針對性的優(yōu)化。
?剛才也提到,硬件性的自然速度就不如過去幾年了,是將來軟件性能調(diào)優(yōu),可能也是提前性能提升性能提升的有關(guān)鍵驅(qū)動力。
?再一個就是要構(gòu)建你的技術(shù)棧, CPU微體系結(jié)構(gòu), v 架構(gòu),還有閱讀匯編代碼,還有操作指令測算系統(tǒng)的一些內(nèi)核的機(jī)制,還有一些編譯優(yōu)化的選項(xiàng)等等。
?再就是避免性能的誤區(qū),一定要去通過測量,通過以實(shí)時數(shù)據(jù)的角度來去指導(dǎo)你性能的一個分析,還有優(yōu)化
?善于使用他們剛才提到的這些工具,進(jìn)行代碼中的一個性能分析,先練習(xí)修復(fù)他們。
審核編輯:劉清
-
處理器
+關(guān)注
關(guān)注
68文章
19748瀏覽量
232966 -
dsp
+關(guān)注
關(guān)注
555文章
8123瀏覽量
354387 -
數(shù)據(jù)處理
+關(guān)注
關(guān)注
0文章
625瀏覽量
28965 -
硬件加速器
+關(guān)注
關(guān)注
0文章
42瀏覽量
12964
原文標(biāo)題:朱金鵬:基于CPU性能調(diào)優(yōu)的必要性和方法
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
芯片返修的必要性?
接口電路的必要性
機(jī)床數(shù)控化改造的必要性及其改造方法
安全完整性等級的認(rèn)證的重要性和必要性
機(jī)床數(shù)控化改造的必要性及其改造方法
javajvm調(diào)優(yōu)有幾種方法
鴻蒙開發(fā)實(shí)戰(zhàn):【性能調(diào)優(yōu)組件】

評論