這一次,徹底搞懂 GPU 和CSS 硬件加速
從 cpu 聊
起cpu (central process) 是計算機的大腦,它提供了一套指令集,我們寫的程序最終會通過 cpu 指令來控制的計算機的運行。
cpu 會對指令進行譯碼,然后通過邏輯電路執行該指令。整個執行的流程分為了多個階段,叫做流水線。指令流水線包括取指令、譯碼、執行、取數、寫回五步,這是一個指令周期。cpu 會不斷的執行指令周期來完成各種任務。
指令和數據都會首先加載到內存中,在程序運行時依次取到 cpu 里。cpu 訪問內存雖然比較快,但比起 cpu 執行速度來說還是比較慢的,為了緩解這種速度矛盾,cpu 設計了 3 級緩存,也就是 L1、L2、L3 的緩存。
如圖,多核 cpu 各核心都有自有獨立的 L1、L2 緩存,然后共享 L3 緩存,這 3 級緩存容量是逐漸遞增的,但是速度是逐漸下降的,但是也會比訪問內存快一些。
有了這 3 級緩存以后,cpu 執行速度和訪問內存速度的矛盾就可以得到緩解,不需要一直訪問內存,cpu 每次會加載一個緩存行,也就是 64 字節大小的數據到緩存中。這樣訪問臨近的數據的時候就可以直接訪問緩存。
從內存中把數據和指令加載到 cpu 的緩存中,然后通過控制器控制指令的譯碼、執行,通過運算器進行運算,之后把結果寫回內存。這就是 cpu 的工作流程。
cpu 每個核只有一個線程,也就是單控制流、單數據流。這樣的架構導致 cpu 在一些場景下效率是不高的,比如 3d 渲染的場景。
3d 渲染流程
3d 的渲染首先是建立 3d 的模型,它由一系列三維空間中的頂點構成,3 個頂點構成一個三角形,然后所有的頂點構成的三角形拼接起來就是 3d 模型。
頂點、三角形,這是 3d 的基礎。3d 引擎首先要計算頂點數據,確定 3d 圖形的形狀。之后還要對每個面進行貼圖,可以在每個三角形畫上不同的紋理。
3d 圖形要顯示在二維的屏幕上就要做投影,這個投影的過程叫做光柵化。(光柵是一種光學儀器,在這里就代表 3d 投影到 2d 屏幕的過程)
光柵化要計算 3d 圖形投影到屏幕的每一個像素的顏色,計算完所有的像素之后會寫到顯存的幀緩沖區,完成了一幀的渲染,之后會繼續這樣計算下一幀。
也就是說,3d 渲染的流程是:
- 計算頂點數據,構成 3d 的圖形
- 給每個三角形貼圖,畫上紋理
- 投影到二維的屏幕,計算每個像素的顏色(光柵化)
- 把一幀的數據寫入顯存的幀緩沖區
頂點的數量是非常龐大的,而 cpu 只能順序的一個個計算,所以處理這種 3d 渲染會特別費勁,于是就出現了專門用于這種 3d 數據的并行計算的硬件,也就是 GPU。
GPU 的構成
和 cpu 的一個一個數據計算不同,gpu 是并行的,有成百上千個核心用于并行計算。
gpu 也是有著指令、譯碼、執行的流程,只不過,每個指令會并行執行 n 個計算,是單控制流多數據流的,而 cpu 是單控制流單數據流。
所以,對于 3d 渲染這種要計算成萬個頂點數據和像素點的場景,GPU 會比 CPU 高效很多。
但是,gpu 全是優點么?也不是。
cpu 和 gpu 的區別
cpu 是通用的,能夠執行各種邏輯和運算,而 gpu 則是主要是用于并行計算大批量的重復任務,不能處理復雜邏輯。
如上圖,cpu 中控制器和緩存占據了很大一部分,而 gpu 中這兩部分則很少,但是有更多的核心用于計算。
兩者對比的話,cpu 相當于一個大學生,能夠解決各種難題,但是計算 1 萬個加法就沒那么快,而 gpu 就像一幫小學生,解決不了難題,但是計算加法這種就很快,因為人多。
也就是說如果邏輯復雜,那么只能用 cpu,如果只是計算量大,并且每個計算都比較重復,那就比較適合 gpu。
3d 的渲染中有大量這種重復卻簡單的計算,比如頂點數據和光柵化的像素數據,通過 gpu 就可以并發的一次計算成百上千個。
opengl、webgl、css 硬件加速
顯卡中集成了 gpu,提供了驅動,使用 gpu 能力需要使用驅動的 api。gpu 的 api 有一套開源標準叫做 opengl,有三百多個函數,用于各種圖形的繪制。(在 windows 下有一套自己的標準叫做 DirectX)
我們在網頁中繪制 3d 圖形是使用 webgl 的 api,而瀏覽器在實現 webgl 的時候也是基于 opengl 的 api,最終會驅動 gpu 進行渲染。
css 大部分樣式還是通過 cpu 來計算的,但 css 中也有一些 3d 的樣式和動畫的樣式,計算這些樣式同樣有很多重復且大量的計算任務,可以交給 gpu 來跑。
瀏覽器在處理下面的 css 的時候,會使用 gpu 渲染:
- transform
- opacity
- filter
- will-change
瀏覽器是把內容分到不同的圖層分別渲染的,最后合并到一起,而觸發 gpu 渲染會新建一個圖層,把該元素樣式的計算交給 gpu。
opacity 需要改變每個像素的值,符合重復且大量的特點,會新建圖層,交給 gpu 渲染。transform 是動畫,每個樣式值的計算也符合重復且大量的特點,也默認會使用 gpu 加速。同理 fiter 也是一樣。
這里要注意的是 gpu 硬件加速是需要新建圖層的,而把該元素移動到新圖層是個耗時操作,界面可能會閃一下,所以最好提前做。will-change 就是提前告訴瀏覽器在一開始就把元素放到新的圖層,方便后面用 gpu 渲染的時候,不需要做圖層的新建。
當然,有的時候我們想強制觸發硬件渲染,就可以通過上面的屬性,比如
- will-change: transform;
或者
- transform:translate3d(0, 0, 0);
chrome devtools 可以看到是 cpu 渲染還是 gpu 渲染,打開 rendering 面板,勾選 layer borders,會發現藍色和黃色的框。藍色的是 cpu 渲染的,而黃色的是 gpu 渲染的。
比如這段文字,現在沒有單獨一個圖層:
添加一個 will-change: transform 的屬性,瀏覽器會新建圖層來渲染該元素,然后使用 gpu 渲染:
gpu 硬件加速能減輕 cpu 壓力,使得渲染更流暢,但是也會增加內存的占用,對于 transform、opacity、filter 默認會開啟硬件加速。其余情況,建議只在必要的時候用。
opencl 和神經網絡
重復且大量的計算任務只有 3d 渲染一種場景么?
不是的,AI 領域的機器學習也很典型,它的特點是大量的神經元需要計算,但是每個計算都比較簡單,也很適合用 gpu 來跑。
現在的 gpu 不只是能跑圖形渲染,也提供了一些編程能力,這部分 api 有 opencl 標準。可以通過 gpu 的并行計算能力來跑一些有大量計算但是沒有很多邏輯的的任務,會比 cpu 效率更高。
總結
cpu 提供了指令集,會不斷的執行取指令、譯碼、執行、取數、寫回的指令周期,控制著計算機的運轉。
cpu 計算的速度比較快,而訪問內存比較慢,為了緩和兩者的矛盾,引入了 L1、L2、L3 的多級緩存體系,L1、L2、L3 是容器逐漸變大,訪問速度逐漸變慢的關系,但還是比訪問內存快。內存會通過一個緩存行(64 字節)的大小為單位來讀入緩存,供 cpu 訪問。
3d 渲染的流程是計算每一個頂點的數據,連成一個個三角形,然后進行紋理貼圖,之后計算投影到二維屏幕的每一個像素的顏色,也就是光柵化,最后寫入顯存幀緩沖區,這樣進行一幀幀的渲染。
cpu 的計算是一個個串行執行的,對于 3d 渲染這種涉及大量頂點、像素要計算的場景就不太合適,于是出現了 gpu。
gpu 可以并行執行大量重復的計算,有成百上千個計算單元,相比 cpu 雖然執行不了復雜邏輯,但是卻能執行大量重復的運算。提供了 opengl 的標準 api。
css 中可以使用 gpu 加速渲染來減輕 cpu 壓力,使得頁面體驗更流暢,默認 transform、opacity、filter 都會新建新的圖層,交給 gpu 渲染。對于這樣的元素可以使用 will-change: 屬性名; 來告訴瀏覽器在最開始就把該元素放到新圖層渲染。
gpu 的并行計算能力不只是 3d 渲染可以用,機器學習也有類似的場景,可以通過 opencl 的 api 來控制 gpu 進行計算。
gpu 和前端的關系還是挺密切的,不管是 webgl,還是 css 硬件加速,或者網頁的性能都與之相關。希望這篇文章能夠幫大家了解 gpu 的原理和應用。

































