在了解 GPU 與 GPU 之間的差異前,我們先來看看計算機是如何計算加減乘除的。

以 CPU 計算乘法為例,CPU 需要先去得 Memory 的資料才可以進行運算,計算完成後還需要把結果放入 Memory 中。
接著來看看矩陣乘法。

想想看,如果是電腦會怎麼運作?有很多種可能吧!例如可以重複的做剛剛提到的單一運算式的方法,也可以先將所有資料都先讀取到 CPU,在進行所有運算,最後再將結果儲存回 Memory 中。
想當然爾,盡可以能的減少步驟是一個影響效能的關進因素,但除了減少步驟外,還會有其他可以改進效能的方法嗎?顯然是有的。例如請更多的工人一起參與計算這個這些運算式,也就是平行運算!
假設我們有四個工人,我們可以請他們分別計算其中的一格,如此一來,如果有更多的工人,就可以計算更龐大的矩陣運算!
既然請越多的工人,也就是越多的 CPU 核心,就可以讓電腦運算的更快,那只要不斷新增核心數量不就可以讓電腦越跑越快嗎?但實際上電腦運算有幾個根本限制,舉例來說,不是所有工作都能同時做,很多程式只有一部分可以平行處理,另一部分必須依序執行。
為了解決諸如此類的問題,CPU 的設計並不是完全已平行運算為核心,這也就有了 GPU 的誕生。
運用平行運算的原理,GPU 透過大量核心來實現,如下圖:

https://docs.nvidia.com/cuda/cuda-programming-guide/01-introduction/introduction.html
會發現一個 CPU 的 Control Unit 和 L1 Cache 都是綁定在一個 Core 上,而 GPU 為了應付成千上萬的核心,多個核心會共用一個 Control Unit 和 L1 Cache,這帶來了一些好處與缺點。
既然我們知道 GPU 以數以萬計的運算單元實作的,為了放入更多的計算單元,實際上的運作方式不會是每個核心各自做自己的數學運算,讓我們用費林分類法(Flynn's taxonomy)來說明。
費林分類法是電腦平行處理系統結構的一種常用分類方法,根據資訊流分成指令和資料兩種,分成了四大類,其中 PU(Processing Unit)作為計算一個 Instruction、一個 Data。
| Instruction / Data | Single Instruction | Multiple Instruction |
|---|---|---|
| Single Data | 單指令流單數據流(SISD) | 多指令流單數據流(MISD) |
| Multiple Data | 單指令流多數據流(SIMD) | 多指令流多數據流(MIMD) |

要讓一個核心「獨立作業」,它不能只有負責計算的 ALU(算術邏輯單元),還需要一整套負責處理指令與協調運作的控制電路。這些電路的功能包括:
如果 GPU 的上萬個核心都各自跑各自的,就代表每一個核心都需要一整套完整的指令處理與控制電路。這會消耗大量的晶片面積,使得晶片上能放置的 ALU 數量大幅減少。
透過 SIMD 的設計,可以讓多個計算單元共用同一套控制邏輯,因此能節省大量控制電路的空間,把更多晶片面積用來放置 ALU。
歸約(Reduction)是一種常見的計算模式,其核心概念是將一組資料透過某種結合運算逐步合併,最終得到單一結果。常見的運算包括加法、最大值、最小值等。
其中,累加(Summation)是最典型的歸約運算之一,其運算子為加法,目標是將一組數值合併為一個總和。
因此,在沒有平行化的情況下,累加運算會形成一條序列化的計算鏈。假設共有 8 個元素需要累加,則需要進行 7 次加法運算,也就是 7 個時間單位 才能完成,如下圖所示。這正體現了在序列實作中,歸約運算因為資料相依性而無法同時進行多個計算。

看似每個結果都會依賴前一個的結果,但我們其實可以把這八個數字拆成四個部分,在分別相加,最後會四個答案,在繼續兩兩一組,直到最後算出答案,如下圖:

這個演算法就是 Reduction Algorithm,將「多個元素的集合」透過可結合結合律(Associativity)的特性,反覆合併,這樣只需要 3 個步驟 ($log_2N$) 就能得到最終結果。
在單位時間內,都是做加法運算,但處理的是不同的資料,這就是經典的 SIMD 應用。
想要達到看似平行處理的效果,有兩種方式,分別是並行(Concurrent) 與平行(Parallel)。
Concurrent 意味著:多個任務在單個核心中頻繁地切換任務,達成看似多個任務同時處理的效果。
Parallel 意味著:多個任務在各不同的核心中執行,實現真正的平行化處理。

https://www.youtube.com/watch?v=RlM9AfWf1WU
當沒有 Concurrent 與 Parallel 意味著需將不同任務以順序的方法執行,透過 Concurrent 會將任務拆分成不同時間段給單個核心執行,Parallel 則是兩個核心執行各執行一個任務,最後我們也可以兩個核心執行四個任務,透過 Concurrent 與 Parallel 的方法。
GPU 的發展主有有兩大領域,分別是「圖形渲染」與「高效能 GPU 計算」。

廠商會根據不同的場景設計 GPU,軟體開發商為了能夠兼容不同的 GPU,需要一種通用的 API,讓軟體開使用,常見的包括 OpenCL、OpenGL、Direct 3D、HIP。這些定義如何表達程式的模型,我們稱之為 Programming Model。

Programming Model 是一種抽象化的思維框架,用來描述「程式應該如何表達計算、資料如何被組織,以及計算單元之間如何互動」,簡單來說,它就像是一種解題的招式,開發者可以透過開發商定義的招式實作想要的功能。
它不等同於程式語言,而是介於演算法與實際硬體/系統之間的一層設計概念,這看起來非常抽象,讓我們實際透過一些例子來理解他吧!