原來 CPU 爲程序性能優化作了這麼多

本文主要來學習內存屏障和 CPU 緩存知識,以便於咱們去了解 CPU 對程序性能優化作了哪些努力。緩存

首先來看下 CPU 緩存:性能優化

CPU 緩存

CPU 緩存是爲了提升程序運行的性能,CPU 在不少處理上內部架構作了不少調整,好比 CPU 高速緩存,你們都知道由於硬盤很慢,能夠經過緩存把數據加載到內存裏面,提升訪問速度,而 CPU 處理也有這個機制,儘量把處理器訪問主內存時間開銷放在 CPU 高速緩存上面,CPU 訪問速度相比內存訪問速度又要快好多倍,這就是目前大多數處理器都會去利用的機制,利用處理器的緩存以提升性能。服務器

多級緩存

CPU 的緩存分爲三級緩存,因此說多核 CPU 會有多個緩存,咱們首先來看下一級緩存(L1 Cache):多線程

L1 Cache 是 CPU 第一層高速緩存,分爲數據緩存和指令緩存,通常服務器 CPU 的 L1 緩存的容量一般在 32-4096 KB。架構

因爲 L1 級高速緩存容量的限制,爲了再次提升 CPU 的運算速度,在 CPU 外部放置-高速存儲器,即二級緩存(L2 Cache)。性能

由於 L1L2 的容量仍是有限,所以提出了三級緩存,L3 如今的都是內置的,它的實際做用便是,L3 緩存的應用能夠進一步下降內存延遲,同時提高大數據量計算時處理器的性能,具備較大 L3 緩存的處理器提供更有效的文件系統緩存行爲及較短消息和處理器隊列長度,通常是多核共享一個 L3 緩存。學習

CPU 在讀取數據時,先在 L1 Cache 中尋找,再從 L2 Cache 尋找,再從 L3 Cache 尋找,而後是內存,再後是外存儲器硬盤尋找。大數據

以下圖所示,CPU 緩存架構中,緩存層級越接近 CPU core,容量越小,速度越快。CPU Cache 由若干緩存行組成,緩存行是 CPU Cache 中的最小單位,一個緩存行的大小一般是 64 字節,是 2 的倍數,不一樣的機器上爲 32 到 64 字節不等,而且它有效地引用主內存中的一塊地址。優化

CPU 緩存架構

多 CPU 讀取一樣的數據進行緩存,進行不一樣運算以後,最終寫入主內存以哪一個 CPU 爲準?這就須要緩存同步協議了:spa

緩存同步協議

在這種高速緩存回寫的場景下,有不少 CPU 廠商提出了一些公共的協議-MESI 協議,它規定每條緩存有個狀態位,同時定義了下面四個狀態:

  • 修改態(Modified):此 cache 行已被修改過(髒行),內容已不一樣於主存,爲此 cache 專有;
  • 專有態(Exclusive):此 cache 行內容同於主存,但不出現於其它 cache 中;
  • 共享態(Shared):此 cache 行內容同於主存,但也出現於其它 cache 中;
  • 無效態(Invalid):此 cache 行內容無效(空行)。

多處理器,單個 CPU 對緩存中數據進行了改動,須要通知給其它 CPU,也就是意味着,CPU 處理要控制本身的讀寫操做,還要監聽其餘 CPU 發出的通知,從而保證最終一致

運行時的指令重排

CPU 對性能的優化除了緩存以外還有運行時指令重排,你們能夠經過下面的圖瞭解下:

好比圖中有代碼 x = 10;y = z;,這個代碼的正常執行順序應該是先將 10 寫入 x,讀取 z 的值,而後將 z 值寫入 y,實際上真實執行步驟,CPU 執行的時候多是先讀取 z 的值,將 z 值寫入 y,最後再將 10 寫入 x,爲何要作這些修改呢?

由於當 CPU 寫緩存時發現緩存區正被其餘 CPU 佔用(例如:三級緩存),爲了提升 CPU 處理性能,可能將後面的讀緩存命令優先執行

指令重排並不是隨便重排,是須要遵照 as-if-serial 語義的,as-if-serial 語義的意思是指無論怎麼重排序(編譯器和處理器爲了提升並行度),單線程程序的執行結果不能被改變。編譯器,runtime 和處理器都必須遵照 as-if-serial 語義,也就是說編譯器和處理器不會對存在數據依賴關係的操做作重排序

那麼這樣就會有以下兩個問題:

  1. CPU 高速緩存下有一個問題:

緩存中的數據與主內存的數據並非實時同步的,各 CPU(或 CPU 核心)間緩存的數據也不是實時同步。在同一個時間點,各 CPU 所看到同一內存地址的數據的值多是不一致的

  1. CPU 執行指令重排序優化下有一個問題:

雖然遵照了 as-if-serial語義,僅在單 CPU 本身執行的狀況下能保證結果正確。多核多線程中,指令邏輯沒法分辨因果關聯,可能出現亂序執行,致使程序運行結果錯誤。

如何解決上述的兩個問題呢,這就須要談到內存屏障

內存屏障

處理器提供了兩個內存屏障(Memory Barrier) 指令用於解決上述兩個問題:

寫內存屏障(Store Memory Barrier):在指令後插入 Store Barrier,能讓寫入緩存中的最新數據更新寫入主內存,讓其餘線程可見。強制寫入主內存,這種顯示調用,CPU 就不會由於性能考慮而去對指令重排。

讀內存屏障(Load Memory Barrier):在指令前插入 Load Barrier,可讓高速緩存中的數據失效,強制重新的主內存加載數據。強制讀取主內存內容,讓 CPU 緩存與主內存保持一致,避免了緩存致使的一致性問題。

Java 中也有相似的機制,好比 Synchronizedvolatile 都採用了內存屏障的原理。

總結

本文主要介紹了在提升程序運行性能上,CPU 做出了哪些優化:緩存和運行時指令重排,最後還介紹了內存屏障相關知識。

參考

http://dwz.win/7ps

相關文章
相關標籤/搜索