性能優化技巧之系統層次優化
編者按:系統層次的優化有很多方法,如利用Cache、 Lazy computing、Read ahead等等,對系統層次進行優化,效果也是比較明顯的。本文將會討論一些常見的系統優化的方法。
從系統層次去優化系統往往有比較明顯的效果。但是,在優化之前,我們先要問一問,能否通過擴展系統來達到提高性能的目的,比如:
- Scale up: 用更強的硬件替代當前的硬件
- Scale out: 用更多的部件來增強系統的性能
使用更強的硬件當然和優化沒有半點關系,但是如果這是一個可以接受的方案,為什么不用這個簡單易行的方案哪?替換硬件的風險要比改架構,改代碼的風險小多了,何樂而不為?
Scale out的方案就有一點麻煩。它要求系統本身是支持scale out,或者把系統優化成可以支持scale out。不管是哪一種選擇,都不是一個簡單的選擇。設計一個可以scale out的系統已經超出了本文所要關注的范圍,但是,scale out應該是系統優化的一個重要方向。
下面會討論一些常見的系統優化的方法,如果還有其他沒有提到的,也歡迎讀者指出來。
1) Cache
- Cache是什么?Cache保存了已經執行過的結果。
- Cache為什么有效?一是可以避免計算的開銷(比如SQL查詢的開銷);二是離計算單元更近,所以訪 問更快(比如CPU cache)。
- Cache的難點在哪里?一是快速匹配,這涉及到匹配算法選擇(一般用哈希表),Cache容量(哈希表的容量影響查找速度);二是替換策略(一般使用LRU或者隨機替換等等)。
- Cache在哪些情況下有效?毫無疑問,時間局部性,也就是當前的結果后面會用到,如果沒有時間局部性,Cache就不能提高性能,反而對性能和系統架構有害處。所以在系統設計之初,最好是審視一下數據流程,再決定是否引入Cache層。
2) Lazy computing
Lazy computing(延遲計算),簡而言之,就是不要做額外的事情,特別是無用的事情。最常見的一個例子就是COW(copy on write),可以參考這個鏈接http://en.wikipedia.org/wiki/Copy-on-write。
- COW是什么?寫時復制。也就是說fork進程時,子進程和父進程共享相同的代碼段和數據段,如果沒有寫的動作發生,就不要為子進程分配新的數據段(通常在fork之后,會有exec,用新的代碼段和數據段替換原來的代碼段和數據段,所以復制父進程的數據段是沒有用的)。
- COW為什么有效?一是可以節省復制內存的時間,二是可以節省內存分配的時間(到真正需要時再分配,雖然時間不會減少,但是CPU的使用更加均勻,避免抖動)。
- COW的難點在哪里?一是引用計數,多個指針指向同一塊內存,如果沒有引用計數,內存無法釋放;二是 如何知道哪塊內存是可以共享的?(在fork的例子里面,父進程,子進程的關系非常明確,但是在有些應用里面,需要查找能夠共享的內存,查找需要花時間)
Lazy computing在哪些情況下有效?目前能想到的只有內存復制。用時分配內存算不算哪?用時分配內存不能節省時間,但是可以節省空間。靜態內存對時間性能有好處;動態內存對空間性能有好處。就看目標是優化哪個性能了。
3) Read ahead
Read ahead (預讀),也可以稱之為pre-fetch(預取)。就是要提前準備所需要的數據,避免使用時的等待。
- Read ahead是什么?可以參考http://en.wikipedia.org/wiki/Readahead,這個是講文件預讀的。CPU里面也有pre-fetch(CPU預取需要仔細安排,最好是能夠填充流水線,所以需要多次嘗試才有結果)。
- Read ahead為什么有效?Read ahead可以減少等待內存的時間。其實相當于把多個讀的動作集合成一個。這個和網絡里面的buffering或者sliding window有異曲同工之妙。停-等協議是最簡單的,但是效率也最低。
- Read ahead的難點在哪里?預讀多少才合適?預讀窗口的大小需要根據負載,文件使用的多少等因素動態調整。預測的成功與否關系的性能。所以這并不是一個簡單的優化方法。
- Read ahead在哪些情況下有效?毫無疑問,空間局部性。沒有空間局部性,read ahead就失去了用武之地。用錯了,反而會降低性能。
4) Hardware assist
Hardware assist (硬件輔助),顧名思義,就是用硬件實現某些功能。常見的,比如加密,解密;正則表達式或者DFA engine,或者規則查找,分類,壓縮,解壓縮等等。邏輯簡單,功能確定,CPU intensive的工作可以考慮用硬件來代替。
- Hardware assist為什么有效?協處理器可以減輕CPU的工作,而且速度比CPU做要快(這個要看情況,并不是任何情況下都成立)。Hardware assist和Hardware centric的設計完全不同,不能混為一談。在Hardware assist的設計里面,主要工作還是由軟件完成;而hardware centric就是基于ASIC的設計方案,大部分工作是有硬件來完成。
- Hardware assist的難點在哪里?一是采用同步還是異步的方式與硬件交互(通常是異步);二是如何使硬件滿負荷工作,同時又避免緩沖區溢出或丟棄(這個要安排好硬件和軟件的節奏,使之協調工作);還有就是硬件 訪問內存的開銷(盡量硬件本身所帶的內存,如果有的話)。
5) Asynchronous
Asynchronous(異步)。同步,異步涉及到消息傳遞。一般來說,同步比較簡單,性能稍低;而異步比較復雜,但是性能較高。
- Asynchronous是什么?異步的含義就是請求和應答分離,請求和應答可以由不同的進程或線程完成。比如在 TCP協議的實現里面,如果滑動窗口是1,那么每次只能發送一個字節,然后等待應答;如果增加滑動窗口,那么一次可以發送多個字節,而無需等待前一個字節的應答。這樣可以提高性能。
- Asyncrhonous為什么有效?異步消除了等待的時間,可以更有效利用帶寬。
- Asynchronous的難點是什么?一是如何實現分布式的狀態機?由于請求和應答雙方是獨立的,所以要避免狀態之間有依賴關系,在無法消除狀態之間的依賴關系時,必須使用同步消息(比如三次握手);二是應答來了之后, 如果激活原來的執行過程,使之能夠繼續執行。
- Asynchronous在哪些情況下有效?很明顯,狀態之間不能有依賴關系,同時需要足夠的帶寬(或者窗口)。
6) Polling
Polling(輪詢)。Polling是網絡設備里面常用的一個技術,比如Linux的NAPI或者epoll。與之對應的是中斷,或者是事件。
- Polling為什么有效?Polling避免了狀態切換的開銷,所以有更高的性能。
- Polling的難點是什么?如果系統里面有多種任務,如何在polling的時候,保證其他任務的執行時間?Polling 通常意味著獨占,此時系統無法響應其他事件,可能會造成嚴重后果。
- Polling在哪些情況下有效?凡是能用事件或中斷的地方都能用polling替代,是否合理,需要結合系統的數據流程來決定。
7) Static memory pool
Static memory pool(靜態內存)。如前所述,靜態內存有更好的性能,但是適應性較差(特別是系統里面有多個 任務的時候),而且會有浪費(提前分配,還沒用到就分配了)。
- Static memory pool為什么有效?它可以使內存管理更加簡單,避免分配和是否內存的開銷,并且有利于調試內存問題。
- Static memory pool的難點在哪里?分配多大的內存?如何避免浪費?如何實現O(1)的分配和釋放?如何初始化內存?
- Static memory pool在哪些情況下有效?一是固定大小的內存需求(通常與系統的capacity有關),內存對象的大小一致,并且要求快速的分配和釋放。
系統層次的優化應該還有很多方法,能想起來的就這么多了(這部分比較難,醞釀了很久,才想起來這么一點東西^-^),讀者如果有更好的方法,可以一起討論。性能優化是關注實踐的工作,任何紙上談兵都是瞎扯,與讀者共勉。
【編輯推薦】