運算規模的擴展可分為強擴展和弱擴展,前者保持問題大小不變,後者隨著任務數量增加問題大小也增加。強擴展的效能受限於通訊時間與運算時間的比率,弱擴展則受同步開銷影響。
人工智慧(AI)帶動高效能運算的發展,本文將介紹平行運算(Parallel Computing)的概念、共享式(Shared)與分散式(Distributed)記憶體架構之差異,以及平行運算系統效能的衡量和運算規模的擴展(Scaling)。
平行運算系統
一言以蔽之,平行運算就是能夠同時執行許多個運算作業。但平行運算系統的時間和人類使用的時鐘時間是不一樣的。最小的運算時間單位為一個滴答(Clock Tick)或一個時脈週期(Clock Cycle),它可以用來代表擷取資料、執行運算或通訊所需的最少時間。從技術面而言,一個滴答是與一個指令所需的狀態變化有關。此狀態可以是處理器狀態、資料狀態、記憶體狀態或控制訊號。在一個時脈週期內,可以執行一條完整的指令、或部分指令,或多條指令。
CPU每秒能支援的狀態變化數量是有限的,例如:一顆3GHz的CPU每秒只能支援30億次的狀態變化量,這就限制了CPU的運算速度。因為每一個時脈週期都會產生熱,運算速度越快,CPU產生的熱就越多,最終會損壞CPU。所以,必須利用平行運算來解決這個問題。其目標是:降低記憶體讀寫的延遲時間、增加記憶體頻寬、提高運算速率。使用平行運算程式來測量系統效能時,通常是以人類使用的時鐘來測量,因為這包含了即時的必要延遲,例如:記憶體延遲、通訊延遲,它們無法直接轉換為時脈週期來計算。平行運算系統包含了下列的實體單元(硬體):
- 節點(Node):節點是由網路連接的獨立實體電腦單元,通常運行著各自的作業系統。因此,辦公室中的工作站或筆記型電腦就是一個節點。一個節點可以包含數個處理器晶片。數個節點組成一個群集(Cluster)。目前,除了CPU以外,包含了GPU或TPU等特殊加速器硬體的節點也越來越普遍。如圖1,其節點就包含了一個GPU。
- 處理器晶片:其內部包含了數個處理單元,稱之為核心(Core)。
- 核心:每個核心都能夠執行獨立的執行緒(Thread of Execution)。
圖1 一個節點內含有四顆處理器晶片,且每顆處理器晶片內含八個核心,並共用一個記憶體池和一個GPU
非實體單元(軟體)則包含了執行緒和行程(Process)。執行緒是一組CPU指令,作業系統會將它視為獨立的單一指令組。一個行程可能包含數個執行緒,或管理數個執行緒的資源,這些資源包含記憶體、檔案、通訊埠、其它硬體裝置。單一程式可以在相同或不同系統或節點上的多個核心上運行。在設計平行運算系統時,必須考慮三個關鍵:(1)執行緒如何運行?(2)如何將記憶體分配給這些執行緒?(3)數個執行緒之間如何通信?尤其是當它們需要更新相同資料時。特別要注意的是,上述三項是互相關聯的。執行緒的運行方式有兩種:串列(Serial)和平行。串列運算是使用單一執行緒在任一時間點處理單一資料。平行運算是同時處理的機制,包含有:
- 單一執行緒:只使用一個執行緒,並同時對多個資料或CPU的向量指令,進行操作。
- 協同工作:單一行程中包含了多個執行緒。
- 獨立工作:多個行程可共用相同節點或多個節點,來處理相同的資料。
- 上述三種的組合。
記憶體的存取有下列三種模式:
- 共享式記憶體:當程式在同一系統上的多個核心運行時,亦即單一行程中包含了多個執行緒,行程中的每個執行緒都可以存取同一虛擬位址中的記憶體。
- 分散式記憶體:當程式使用多個行程時,且無論這些行程是在單一節點上,或跨越不同的節點,就需採用分散式記憶體設計。在這種架構中,每個行程都擁有一部份資料,其他行程必須向檔案擁有者發送訊息來更新它們各自的資料。即使多個行程在單一節點上運行,每個行程也都有它們各自的虛擬記憶體空間。因此,此類行程之間必須使用分散式記憶體架構的程式介面來通訊。
- 混合模式:可以在相同或不同節點上運行的多執行緒行程(Multithreaded Process),是透過共享式記憶體架構的程式介面,在單一節點上使用多個核心。同時,它們也可以採用分散式記憶體架構的程式介面與其他節點上的行程進行協調。
通訊機制取決於記憶體架構。在共享式記憶體架構中,OpenMP等應用程式介面能支援執行緒之間的通訊,這些執行緒共享記憶體和資料。另一方面,在分散式記憶體架構中,訊息傳遞介面(Message Passing Interface, MPI)可支援行程之間的通訊,這些行程在相同或不同節點上執行。
系統效能的衡量
衡量一個平行運算系統有許多種方法,但此處只討論效率和加速比(Speedup)。效率是指在運算期間,系統可用的所有資源中,被用於運算的比例。它是透過將實際資源利用率與最高性能進行比較來確定的,即最佳資源利用率。下面列出於衡量系統效能時,常用的專有名詞和其意義:
- 處理器的實際使用率:在特定期間內,執行的浮點運算(Floating Point Operation, FLOP)數量。
- 最高性能:假設每個處理器核心在每個時脈週期內,執行最多的FLOP。
- 平行程式的效率:實際的每秒浮點運算(Floating Point Operation Per Second, FLOPS)數量與可能的最高性能的比率。
- 加速比:用於評估效率。其測量公式為:加速比=串列運算所需的時間/平行運算所需的時間。
- 當程式受到處理器運算速度的限制時,加速比不能大於平行運算資源的數量。所以,對於一個平行程式的效率而言,其有效測量的定義為:平行程式的效率=加速比/核心數量。因此,優化平行程式的目標是使其加速比盡可能接近核心數量,最高效率能夠趨近100%。
假設有一個程式,以串列運算所需的時間是300秒。改用具50顆核心的平行運算系統執行,所需的時間是6秒。所以,其加速比是300/6=50倍,平行程式的效率是50/50=1。這是一個理想範例,因為工作負載完全被平行處理,並且所有核心都有被充份利用。
平行運算的擴展
高效能運算(HPC)、平行運算都和運算規模息息相關。運算規模的擴大會增加問題的大小,或平行任務的數量,如圖2。程式的可擴展性是指執行程式所花費的時間是如何隨著問題大小和處理器數量的變化而變化的。實際上,這是一個複雜的關係,取決於問題的特徵、演算法的選擇和底層硬體和網路。按照任務的數量或問題的大小,平行運算可區分成兩種:強擴展(Strong Scaling)和弱擴展(Weak Scaling)。
圖2 以三維座標表示平行運算的問題大小、任務數量和處理時間之間的關係
強擴展將增加平行任務的數量,同時保持問題大小不變(圖3)。然而,即使增加運算單元(核心、處理器或節點)的數量來平行處理更多任務,這些單元和主機之間的通訊也會產生額外的延遲,例如:傳送和接收資料所花費的時間。強擴展的效能是由通訊時間對運算時間的比率決定的,當該比率越大時,就表示強擴展的效能越差。
圖3 強擴展的平行運算系統
在理想的情況下,執行時間會隨著平行任務數量的增加而減少。但如果使用強擴展執行的程式碼沒有變得更快,則表示可能使用了太多的任務或處理器在重覆執行相同的工作,如圖4。在強擴展的情況下,平行程式的加速是受到程式中無法平行化的部份影響,即受程式中串行(Serial)部份的限制。
圖4 理想的和不理想的強擴展之比較
弱擴展是指問題大小隨著任務數量的增加而增加,因此每個任務的運算量能維持不變,如圖5。如果程式具有良好的弱擴展效能,則可以在相同的時間內,在兩倍的節點上運行兩倍大的問題。然而,同步開銷是影響弱擴展效能的主要因素。同步開銷是指在平行運算環境中,協調和管理平行任務所需的額外時間和運算資源。這種開銷是由於需要確保多個平行任務或執行緒能正確且一致地運行,特別是當它們需要存取共享資源或資料時。
圖5 弱擴展的平行運算系統
在弱擴展的情況下,隨著問題大小和節點數量的增加,所需的同步量也會增加,導致程式執行時間的增加,這會降低平行程式的效率,並限制其可擴展性。為了獲得良好的弱擴展效能,必須降低同步開銷。如圖6,在理想的弱擴展情況下,不論問題大小和任務數量,程式處理所需的時間都一樣。反之,在弱擴展效能差的情況下,程式處理所需的時間可能隨著任務數量的增加而增加。
圖6 理想的和不理想的弱擴展之比較
因為強擴展和弱擴展的定義不同,它們的應用也不同,在本質上是無法分辨孰優孰劣。它們的適用性取決於應用程式的特定要求和目標。弱擴展的平行運算非常適用於問題大小是隨著處理器數量增加而自然增加的應用,例如:大數據分析、科學模擬和分散式運算。而強擴展的平行運算非常適用於問題大小保持不變,且必須減少程式執行時間的應用,例如即時處理或對時間很敏感的運算、高效能運算。
此外,並不是所有的指令串流或資料串流都能進行平行運算。譬如可能很難讓某些串列作業(Sequential Operation)進行平行運算。平行化取決於多個指令串流,且或多個資料串流,在CPU、GPU中的單指令多資料流(Single Instruction/Multiple Data, SIMD)是透過向量化實現的。可以透過SIMD來了解什麼才可以平行化。向量化是一種程式設計技術,數個作業是一次就使用全部的陣列,而不是逐一處理單一元素。它是利用處理器中的向量單元來實現的,此向量單元包括了向量暫存器和向量指令。