我們可以多種方式利用晶片內建多處理功能,提高SoC的效能。工作靜態分解(根據輸入資料或處理功能)的效率可能很高,但也非常缺乏彈性。SMP平台與軟體通常只需要稍微修改應用程式碼,甚至完全不需修改,可以提供彈性極高的高效能運算平台,且比單一處理器快得多。多執行緒和SMP平行處理可以發揮相輔相成的效果,充分運用各處理器的管線資源。
單處理器vs.多處理器
在現今特定的建構技術下,將單一處理器的效能提升至理論上最高值既不簡單,也缺乏有效率的做法。更快的時脈、更大的通道、與更大的快取記憶體都是業界嘗試過有效的技術,但相隨而來的是晶片面積與散熱的成本。要搾出最後10%的效能,投資報酬率實在不高。有時候我們別無選擇,只好提高時脈,並將電源與冷卻子系統升級;但如果能將工作分配給多顆處理器,整體效能會提升,且處理器元件的設計也可以更簡單、更有效率。
雖然這是當今PC、伺服器、與工作站的作法,但也適用於嵌入式系統單晶片(SoC)設計。事實上,現在許多嵌入式SoC設計都運用多個處理器,但是在針對特定應用都利用「鬆散耦合」(loosely coupled)的方式。即使到今日,能與軟體無間配合的多處理器SoC設計還是十分有限。但隨著MIPS32 1004K Coherent Processing System(CPS)等SoC設計組件的誕生,系統架構設計師終於得以選擇在單一作業系統下的晶片內建對稱多工處理功能(Symmetric Multiprocessing, SMP),本文將詳述此新技術的功能與限制。
平行處理參考範例
運用平行處理器需要平行軟體支援,軟體工程師會對「平行程式設計」模式有所疑慮,因為不是所有現存的程式碼都針對平行處理平台所編寫。但平行處理軟體有好幾種範例可以參考,軟體設計師也已經相當熟悉其中一些範例,即使他們不見得認為自己很熟悉。
資料平行演算法
資料平行演算法是解決單一基本運算所產生出問題的方法之一,透過分割資料群組,並使用一個以上的處理器,最理想的狀況是有充足的中央處理器(CPU)。大型輸入檔案或資料陣列是典型所謂的龐大資料組,但在嵌入式系統中,這可能意味著高I/O效能與服務頻寬。在部分SoC架構中,多個輸入資料來源,例如網路介面連接埠,每個資料來源都要以同樣的方式處理,可以用統計的方法指派給多個處理器,執行相同的驅動程式/路由器程式碼,自然形成資料平行處理。
要以多個處理器來處理單一資料陣列或單一輸入串流,經常可使用「個個擊破」(divide and conquer_式資料平行處理演算法,例如圖一的簡單範例。這些演算法對單一處理器經常不盡理想,但它們運用延展性,善用運算頻寬,彌補了效率不彰的問題。它們「以量取勝」。這些演算法是平行運算中最具延展性的,但將可運作的循序程式改寫成資料平行處理演算法可能簡單、也可能很困難、甚至不可能,這要視程式相依特性等因素而定。
圖1 – 資料平行處理程式設計模式
如果應用中的絕大部分運算都由少數冗長但有規律的運算loop組成,希望提高現有應用效能的系統設計師最可能直接使用資料平行處理演算法。
隨著PC、工作站、與伺服器用多核心“x86”處理器晶片的興起,業界開始研究與投資在新一波的程式庫與工作套件上,試圖支援並更輕易地在為數不多的處理器上運用平行處理演算法。這包括很多開放程式碼的設計,可針對MIPS等嵌入式架構改寫。支援資料平行處理C/C++與FORTRAN的gcc OpenMP正在成為標準GNU編譯程式系列的一部份。
控制平行處理程式設計
另外一種演算法可稱為控制平行處理程式設計(control-parallel programming),將程式根據工作而非輸入加以分割。我們可將資料平行處理演算法比喻為一家汽車工廠裡,100位工人每一位個別負責製造一輛汽車。而控制平行處理程式則相當於一家工廠裡只有一條生產線,但有100個分站,每個分站都有一位工人,執行不同的工作,佔組裝工作的1/100。圖2說明簡單的2站建構。生產線的作法通常效率較高,但能將組裝分割成多少工作、分配多少工人數還是有所限制。該限制對程式碼很重要,因為可能被延展到數千個處理器,但這對消費電子應用所採用的有限平行SoC架構通常不是問題。
即使不考量到平行處理,軟體工程師通常也將程式分為幾個階段,以簡化程式設計團隊的程式撰寫、除錯與維護,也能降低指令記憶體與快取記憶體的負擔。在很多狀況下,問題的控制平行分解已經提升到OS可見(OS-visible)工作的層次。類似UNIX系統的單一“cc”指令會循序呼叫C語言前置處理器、編譯程式、組譯器、與連結程式。在SMP多處理器上,有些步驟可以同時執行,下一個程式將上一階段的輸出作為輸入,使用檔案,甚至可以用軟體管道(pipe),其一直都是類似UNIX作業系統,像是Linux的功能之一。
當尚未分解成獨立執行的工作時,必須採用軟體工程的作法,讓作業系統與基礎硬體瞭解應用的階段,並在「所有權」交給下一個階段時,清楚地轉換資料處理。和資料平行處理分解時不同的是不需要重新思考或重新設計組成階段。可透過檔案、插座、或管道溝通的程序,粗略地分解工作。如果要更精細地掌控,通常會使用名為pthreads的POSIX執行緒API,各種作業系統都支援該作法,包括Linux、Microsoft Windows與許多即時作業系統。
要做的事越多,同時處理能力也變得更重要
如圖3所示,雖然非當初設計目標,複雜、模組化、多工的嵌入式軟體系統經常展現「意外的」並行性。系統的整體任務可能包括執行多個工作,每一個工作都有不同責任,針對獨特的輸入組合做出回應。如果沒有分時作業系統,這些工作必須各自在獨立的處理器上執行。在分時單處理器上,它們交替地運用時間段落執行。如果以多處理器配合SMP作業系統,它們可以同時在所有可用的處理器上執行。
分散式處理
另一種形式的平行處理也變得很常見,雖然有時候它甚至不被視為「平行」,這就是分散式運算,網路主從式架構是最常見的型態。主從式程式設計基本上是分解控制流,程式工作不自行執行所有運算,而是將工作要求傳送給系統中一或多個各有專職的工作。雖然主從式程式設計使用跨LAN與WAN的情形最為普遍,SMP SoC內工作之間的溝通也使用同樣的方式。我們可使用未修改的主從式二進位檔案(binaries),透過晶片內建或null “loopback”網路介面以TCP/IP溝通;或使用當地通訊協定,傳送記憶體中的資料緩衝器,效率還會更高。
就實務而言,上述任何一種技術都可以獨立或配合使用,針對特定應用發揮以SMP為基礎平台的優勢。我們可建構分散式SMP伺服器資料平行處理陣列,每個伺服器都建構控制流程管道。但要使這種設計發揮效率,就必須有很大的工作負荷與資料組。
系統軟體支援是關鍵
在SoC系統中,可將工作以靜態實體分解,分配給處理器,達到平行處理的目的(如一個處理器核心對應一個輸入埠),我們可用硬體將工作分給處理器,這能減少軟體的額外工作與大小,但卻缺乏運作彈性。
如果嵌入式應用可用靜態的方式分解為用戶端和伺服器,並利用晶片內部連結溝通,結合該系統所需的軟體就只剩下傳遞訊息在處理器之間建構共通的通訊協定的程式碼。該訊息傳遞通訊協定可提供某種程度的抽象化(abstraction),讓一或多個處理器執行相同的基礎應用程式碼,但就任何特定組態而言,處理器之間的負載平衡和硬體分割一樣,都是靜態的。如果要追求更有彈性的平行系統程式設計,就必須以軟體將工作分配給共用資源的多處理器系統。
SMP系統的彈性與適應性
顧名思義,SMP作業系統對系統採用「對稱」的觀點。所有處理器都看到同樣的記憶體、同樣的輸入/輸出裝置、與同樣的整體作業系統狀態。因此將程式從一個處理器轉移到另一個變得非常簡單與高效率,如圖4的簡單範例所示,也減輕了負載平衡的難度。不需額外程式設計或系統管理,一組程式可在單一系統上分時多工,在SMP系統可用的CPU上同時執行。SMP排程程式,如Linux的排程程式,將開關處理器,使它們平衡地進展。
如果Linux應用當作多個程序執行,不需修改就能利用SMP平行處理的優勢。在大多數的狀況下,都不需重新編譯;例外是動態連接到non-thread-safe程式庫的二進位檔案。
SMP Linux環境提供多種工具,讓系統設計師調整可用處理器分享工作的方式。工作的優先順序高低可調整,也能限制為在特定的處理器上執行。如果有適當的核心支援,它們可要求使用不同的即時排程方式。
類似UNIX的作業系統向來提供應用程式某種程度的工作排程掌控,即使是單處理器的分時系統。在Linux中,除了傳統的nice shell指令與系統呼叫以外,還有更複雜的機制,可操縱工作優先順序、工作群組、或特定系統使用者,如果有必要懷疑OS決定的話。
此外,在多處理器配置下,每一個Linux工作都有指定哪些處理器能為工作排程的參數。預設值為系統中所有處理器,但就像優先順序一樣,工作可透過taskset shell指令,或明確的系統呼叫,來選擇特定的CPU。
支援SMP
SMP系統思維要求所有處理器都能看到同樣位址的所有記憶體。對簡單、低效能的處理器而言,做到這一點並不困難。我們只要將所有處理器的取得指令與載入/儲存流量都放在同一個記憶與I/O匯流排上即可。但隨著處理器數目增加,這種簡單模式很快就失效了,因為匯流排很快就變成效能瓶頸。即使在單一處理器系統中,因為高效能嵌入式核心對指令與資料頻寬的要求,主記憶體與處理器間都必須使用快取記憶體。
現在每個處理器都有獨立快取記憶體的系統已不再被自動視為SMP。如果記憶體特定位址的最新數值只存在一個處理器的快取記憶體中,就有基本 而且危險的不對稱情況。這時候必須加入快取記憶體同步協定,恢復系統的對稱。在非常簡單的系統中,所有處理器都連接到同一個匯流排上,只要讓所有快取記憶體控制器監控匯流排,瞭解那個快取記憶體上面有特定記憶位址的最後數值。在較先進的系統上,例如MIPS32 1004K CPS,處理器以點對點的方式,透過交換系統而非匯流排連接記憶體。1004K同步管理員(coherence manager)會針對記憶體交易全面實行紀律,並產生必要的介入訊號,就可以維持多個1004K處理器核心之間的快取記憶體同步。因此1004K處理器對記憶體有對稱的觀點,Linux等SMP作業系統可不受限制地轉移工作並動態平衡處理器負荷。
在嵌入式Soc中,整體運算時間相當部分會花在岔斷服務上。這意味著需要控制優異的負載平衡與調整效能,包括可在哪裡執行程式工作,也包括哪裡可以執行岔斷服務。Linux作業系統有“IRQ affinity”的控制介面,讓使用者與程式指定哪些處理器可為特定的岔斷服務。要使用這種功能,介面必須透過下面的系統硬體,指定岔斷給處理器。1004K整體岔斷控制器(global interrupt controller)就能為1004K CPS提供這種功能。
快取同步基礎架構不僅在對稱多處理系統的處理器,也在處理器與輸入/輸出DMA通道都能產生成效。雖然MIPS32等RISC架構有支援以軟體為基礎I/O同步的功能,這需要在每一個I/O DMA作業前後都讓CPU處理DMA緩衝器。這種處理對I/O密集的應用會造成可測量的效能影響。在1004K CPS中,因為透過I/O同步單元連接輸入/輸出DMA與記憶體,可掌控DMA流量,並與同步載入/儲存流程整合,減低軟體負擔。
付出代價 – 並收到成效
1004K同步管理員會維持處理器、輸入/輸出、與記憶體之間流量的秩序,但這樣做會增加處理器存取記憶體的時間週期。通常導致在管道延誤(stall_時,這會導致損失額外處理器週期,因為要等指令或必要的資料填入快取記憶體。但1004K平台使用MIPS32 34K?核心系列率先引進的MIPS多執行緒架構,讓單一核心同時執行數個指令串流。
1004K CPS的每一個核心都能透過虛擬處理元件(VPE)支援兩個硬體執行緒,對作業系統而言,VPE看起來就像是一個CPU。這兩個虛擬處理器分享相同的快取記憶體與功能單元,並在管線上交錯執行。如果一個VPE在等記憶體填寫快取記憶體而造成延誤,另一個VPE能繼續執行,保持管線忙碌。1004K處理器的多執行緒能解決原本會因為同步記憶體子系統的延遲的風險。
因為對軟體而言,1004K處理器的VPE看裡來就像真的處理器,包括有獨立的岔斷輸入,我們可以利用原本管理多個核心的SMP作業系統邏輯,來管理組成的VPE。在最高的系統管理層級,所有VPE都在動作的雙核心1004K系統看起來就像是一套4-way SMP系統。針對利用SMP能力而撰寫會配置的軟體自然能利用多執行緒功能,反之亦然。
由於對系統資源保持對稱,相較於在獨立核心上執行兩個執行緒,兩個執行緒競爭單一處理器管線的效能會比較低,由多執行緒CPU構成的同步叢集相當普遍,所以效能低落的問題在伺服器系統上已存在多年。而1004K的SMP Linux核心可最佳化負載平衡,如果針對要最佳化耗電量,排程程式可以一次將工作載入一個核心的虛擬處理器,其他虛擬處理器可處於低耗電狀態。如果要最佳化效能,可以將工作先分配給不同的核心,只在所有核心都有工作執行時,才將工作交給多個VPE。
結論
我們可以多種方式利用晶片內建多處理功能,提高SoC的效能。工作靜態分解(根據輸入資料或處理功能)的效率可能很高,但也非常缺乏彈性。SMP平台與軟體通常只需要稍微修改應用程式碼,甚至完全不需修改,可以提供彈性極高的高效能運算平台,且比單一處理器快得多。多執行緒和SMP平行處理可以發揮相輔相成的效果,充分運用各處理器的管線資源。MIPS32 1004K Coherent Processing System將MIPS多執行緒與同步SMP整合到單一IP單元,提供高延展性、高密度的嵌入式運算功能。