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