哪個程序經得起這樣的優化?

      網友投稿 1228 2025-04-01

      #? 一、思維導圖


      #? 二、什么是性能優化?

      性能優化指在不影響系統運行正確性的前提下,使之運行得更快,完成特定功能所需的時間更短,或擁有更強大的服務能力。

      ## 關注

      不同程序有不同的性能關注點,比如科學計算關注運算速度,比如游戲引擎注重渲染效率,而服務程序追求吞吐能力。

      服務器一般都是可水平擴展的分布式系統,系統處理能力取決于單機負載能力和水平擴展能力,所以,提升單機性能和提升水平擴展能力是兩個主要方向,理論上系統水平方向可以無限擴展,但水平擴展后往往面臨通信成本飆高(甚至瓶頸),單機處理能力下降的問題。

      ## 指標

      衡量單機性能有很多指標,比如:QPS(Query Per Second)、TPS、OPS、IOPS、最大連接數、并發數等評估吞吐的指標。

      CPU為了提高吞吐,會把指令執行分為多個階段,會搞指令Pipeline,同樣,軟件系統為了提升處理能力,往往會引入批處理(攢包)等,但跟CPU流水線會引起Latency增加一樣,伴隨著系統負載增加也會導致延遲(Latency)增加,可見,系統吞吐和延遲是兩個沖突的目標。

      顯然,過高的延遲是不能接受的,所以,服務器性能優化的目標往往變成:追求可容忍延遲(Latency)下的最大吞吐(Throughput)。

      延遲(也叫響應時間:RT)不是固定的,通常在一個范圍內波動,我們可以用平均時延去評估系統性能,但有時候,平均時延是不夠的,這很容易理解,比如80%的請求都在10毫秒以內得到響應,但20%的請求時延超過2秒,而這20%的高延遲可能會引發投訴,同樣不可接受。

      一個改進措施是使用TP90、TP99之類的指標,它不是取平均,而是需確保排序后90%、99%請求滿足時延的要求。

      通常,執行效率(CPU)是我們的重點關注,但有時候,我們也需要關注內存占用、網絡帶寬、磁盤IO等,影響性能的因素很多,它是一個復雜的問題。

      哪個程序經得起這樣的優化?

      # 三、基礎知識

      能編寫運行正確的程序不一定能做性能優化,性能優化有更高的要求,這樣講并不是想要嚇阻想做性能優化的工程師,而是實事求是講,性能優化既需要扎實的系統知識,又需要豐富的實踐經驗,只有這樣,你才能具備case by case分析問題解決問題的能力。

      所以,相比直接給出結論,我更愿意多花些篇幅講一些基礎知識,我堅持認為底層基礎是理解并掌握性能優化技能的前提,值得花費一些時間掌握這些根技術。

      ## CPU架構

      你需要了解CPU架構,理解運算單元、記憶單元、控制單元是如何既各司其職又相互配合完成工作的。

      你需要了解CPU如何讀取數據,CPU如何執行任務。

      你需要了解數據總線,地址總線和控制總線的區別和作用。

      你需要了解指令周期:取指、譯指、執行、寫回。

      你需要了解CPU Pipeline,超標量流水線,亂序執行。

      你需要了解多CPU、多核心、邏輯核、超線程、多線程、協程這些概念。

      ## 存儲金字塔

      CPU的速度和訪存速度相差200倍,高速緩存跨越這個鴻溝的橋梁,你需要理解存儲金字塔,而這個層次結構思維基于著一個稱為局部性原理(principle of locality),它對軟硬件系統的設計和性能有著極大的影響。

      局部性又分為空間局部性和時間局部性。

      ### 緩存

      現代計算機系統一般有L1-L2-L3三級緩存。

      每個CPU核心有獨立的L1、L2高速緩存,所以L1和L2是on-chip緩存;L3是多個CPU核心共享的,它是off-chip緩存。

      L1緩存又分為i-cache(指令緩存)和d-cache(數據緩存),L1緩存通常只有32K/64KB,速度高達4 cycles。

      L2緩存能到256KB,速度在8 cycles左右。

      L3則高達30MB,速度32 cycles左右。

      而內存高達數G,訪存時延則在200 cycles左右。

      所以CPU->寄存器->L1->L2->L3->內存->磁盤構成存儲層級結構,越靠近CPU,存儲容量越小、速度越快、單位成本越高,越遠離CPU,存儲容量越大、速度越慢、單位成本越低。

      ### 虛擬存儲器(VM)

      進程和虛擬地址空間是操作系統的2個核心抽象。

      系統中的所有進程共享CPU和主存資源,虛擬存儲是對主存的抽象,它為每個進程提供一個大的、一致的、私有的地址空間,我們gdb調試的時候,打印出來的變量地址是虛擬地址。

      操作系統+CPU硬件(MMU)緊密合作完成虛擬地址到物理地址的翻譯(映射),這個過程總是沉默的自動的進行,不需要應用程序員的任何干預。

      每個進程有一個單獨的頁表(Page Table),頁表是一個頁表條目(PTE)的數組,該表的內容由操作系統管理,虛擬地址空間中的每個頁(4M或者8M)通過查找頁表找到物理地址,頁表往往是層級式的,多級頁表減少了頁表的存儲需求,命失(Page Fault)將導致頁面調度(Swapping或者Paging),這個懲罰很重,所以,我們要改善程序的行為,讓它有更好的局部性,如果一段時間內訪存的地址過于發散,將導致顛簸(Thrashing),從而嚴重影響程序性能。

      為了加速地址翻譯,MMU中增加了一個關于PTE的小的緩存,叫翻譯后備緩沖器(TLB),地址翻譯單元做地址翻譯的時候,會先查詢TLB,只有TLB命失才會查詢高速緩存(L1-2-3)。

      ## 匯編基礎

      了解匯編,了解幾種尋址模式,了解數據操作、分支、傳送、控制跳轉指令。

      理解C語言的if else、while/do while/for、switch case、函數調用是怎么翻譯成匯編代碼。

      理解ebp+esp寄存器在函數調用過程中是如何構建和撤銷棧幀的。

      理解函數參數和返回值是怎么傳遞的。

      ## 異常和系統調用

      異常會導致控制流突變,異常控制流發生在計算機系統的各個層次,異??梢苑譃樗念悾?/p>

      中斷(interrupt):中斷是異步發生的,來自處理器外部IO設備信號,中斷處理程序分上下部。

      陷阱(trap):陷阱是有意的異常,是執行一條指令的結果,系統調用是通過陷阱實現的,陷阱在用戶程序和內核之間提供一個像過程調用一樣的接口“系統調用”。

      故障(fault):故障由錯誤情況引起,它有可能被故障處理程序修復,故障發生,處理器將控制轉移到故障處理程序,缺頁(Page Fault)是經典的故障實例

      終止(abort):終止是不可恢復的致命錯誤導致的結果,通常是硬件錯誤,會終止程序的執行。

      ## 內核態和用戶態

      你需要了解操作系統的一些概念,比如內核態和用戶態,應用程序在用戶態運行我們編寫的邏輯,一旦調用系統調用,便會通過一個特定的中斷陷入內核態,通過系統調用號標識功能,不同于普通函數調用,陷入內核態和從內核態返回需要做上下文切換,需要做環境變量的保存和恢復工作,它會帶來額外的消耗,我們編寫的程序應避免頻繁做context swap,提升用戶態的CPU占比是性能優化的一個目標。

      ## 進程、線程、協程

      在linux內核中,進程和線程是同樣的系統調用(clone),進程跟線程的區別:線程是共享存儲空間的,每個執行流有一個執行控制結構體,這里面會有一個指針,指向地址空間結構,一個進程內的多個線程,通過指向同一地址結構實現共享同一虛擬地址空間。

      通過fork創建子進程的時候,不會馬上copy數據,而是推遲到子進程對地址空間進行改寫,這樣做是合理的,此即為COW(Copy On Write),在應用程序開發中,也有大量的類似借鑒。

      協程是用戶態的多執行流,C語言提供makecontext/getcontext/swapcontext系列接口,很多協程庫也是基于這些接口實現的,微信的libco通過hook慢速系統調用(比如write,read)做到靜默替換,非常巧妙。

      ## 鏈接

      C/C++源代碼經編譯鏈接后產生可執行程序,其中數據和代碼分段存儲,我們寫的函數將進入text節,全局數據將進入數據段,未初始化的全局變量進入bss,堆和棧向著相反的方向生長,局部變量在棧里,參數和返回值也會通過棧傳遞。

      想要程序運行的更快,最好把相互調用,關系緊密的函數放到代碼段相近的地方,這樣能提高icache命中性。減少代碼量、減少函數調用、減少函數指針同樣能提高i-cache命中性。

      內聯既避免了棧幀建立撤銷的開銷,又避免了控制跳轉對i-cache的沖刷,所以有利于性能。同樣,關鍵路徑的性能敏感函數也應該避免遞歸函數。

      減少函數調用(就地展開)跟封裝是相違背的,有時候,為了性能,我們不得不破壞封裝和損傷可讀性的代碼,這是一個權衡利弊的問題。

      ## 常識和數據

      CPU拷貝數據一般一秒鐘能做到幾百兆,當然每次拷貝的數據長度不同,吞吐不同。

      一次函數執行如果耗費超過1000 cycles就比較大了(刨除調用子函數的開銷)。

      pthread_mutex_t首次加解鎖大概耗時4000-5000 cycles左右,之后,每次加解鎖大概120 cycles,O2優化的時候100 cycles,spinlock耗時略少。

      lock內存總線+xchg需要50 cycles,一次內存屏障要50 cycles。

      有一些無鎖的技術,比如linux kernel里的kfifo,主要使用了整型回繞+內存屏障。

      # 四、怎么做性能優化(TODO)

      兩個?向:提?運?速度 + 減少計算量。

      性能優化監控先?,要基于數據??基于猜測,要搭建能盡量模擬真實運?狀態的壓?測試環境,在此基于上獲取的profiling數據才是有?的。

      方法論:監控 -> 分析 -> 優化?三部曲

      # 五、幾個具體問題(TODO)

      1. 如何定位CPU瓶頸?

      2. 如何定位IO瓶頸?

      3. 如何定位?絡瓶頸?

      4.如何定位鎖的問題?

      5. 如何提?并發能??

      大家都知道鎖會引入額外開銷,但鎖的開銷到底有多大,估計很多人沒有實測過,我可以給一個數據,一般單次加解鎖100 cycles,spinlock或者cas更快一點。

      使用鎖的時候,要注意鎖的粒度,但鎖的粒度也不是越小越好,太大會增加撞鎖的概率,太小會導致代碼更難寫。

      多線程場景下,如果cpu利用率上不去,而系統吞吐也上不去,那就有可能是鎖導致的性能下降,這個時候,可以觀察程序的sys cpu和usr cpu,這個時候通過perf如果發現lock的開銷大,那就沒錯了。

      任務調度 虛擬化

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:WPS 2013 扁平化設計風格 非凡的辦公體驗 多人協同辦公更輕松
      下一篇:word文檔中如何插入圖表目錄(word中如何做圖表目錄)
      相關文章
      亚洲av午夜精品无码专区| 亚洲国产精品无码AAA片| 国产亚洲精品国看不卡| 中文字幕亚洲男人的天堂网络| 久久久久久亚洲精品| 亚洲中文字幕不卡无码| 亚洲成a人片在线播放| 亚洲精品无码aⅴ中文字幕蜜桃| 亚洲国产激情在线一区| 亚洲综合校园春色| 国产精品亚洲片在线va| 国产精品亚洲片夜色在线| 亚洲中文字幕无码爆乳app| youjizz亚洲| 亚洲性色AV日韩在线观看| 国产亚洲精品成人AA片| 亚洲乱码日产精品一二三| 亚洲人成电影网站色www| 亚洲国产AV一区二区三区四区| 亚洲国产成人综合精品| 女bbbbxxxx另类亚洲| gogo全球高清大胆亚洲| 亚洲精品乱码久久久久久不卡| 2022中文字字幕久亚洲| 中文亚洲AV片在线观看不卡| 国产亚洲一区二区手机在线观看 | 亚洲国产成人AV在线播放 | 亚洲视频在线视频| 亚洲天堂一区在线| 亚洲国产午夜精品理论片在线播放| 国产精品亚洲av色欲三区| 亚洲国产婷婷香蕉久久久久久| 久久精品国产亚洲一区二区三区| 亚洲精品无码久久久影院相关影片| 亚洲AV日韩精品久久久久久久| 久久综合亚洲色一区二区三区| 亚洲制服丝袜精品久久| 亚洲AV无码一区二区三区网址| 亚洲不卡AV影片在线播放| 亚洲中文字幕不卡无码| 亚洲视频精品在线观看|