我們一起聊聊如何利用并行計算挖掘性能極限
在當前的計算機領域中,CPU 單核性能的增長已逐漸停滯,而業務問題的復雜度卻不斷上升。為了更好地解決這一沖突,在 CPU 中增加核數成為了常見的應對方案。在這種情況下,并行設計的重要性日益凸顯,它能夠充分發揮硬件多核的運行性能。
然而,要通過并行設計將計算負載均衡到每個 CPU 核上,并將軟件性能提升至最大化,面臨著諸多挑戰。例如,并行拆分不合理可能導致產品性能不可控甚至惡化,程序員的串行編程慣性思維使得并發同步互斥實現中的故障難以定位,進而導致產品在較長時間內處于不可用狀態。
為了應對這些挑戰,我們首先需要了解并行計算模型。在面對具體的業務問題時,我們需要將其拆分成可并行的邏輯單元,并實現同步交互。并行計算模型可以幫助我們建立對并發系統的抽象模型和基本概念的認識。
具體來說,并行計算模型可以抽象為兩個層次:一是由結構數據和相應的計算邏輯組成并發執行單元,通過組合實現更復雜的業務;二是基于各種手段(如內存、互斥量、消息隊列、數據庫等)對并發執行單元計算的結果進行交互同步,保證業務計算結果的確定性。
需要注意的是,并行執行單元的粒度可大可小,不僅僅局限于線程。在設計并發架構時,我們應根據處理的特定領域問題,選擇合適的并行執行單元粒度,并選擇或定制實現相應的并發調度框架。
接下來,我們介紹六種針對不同業務問題的典型并行設計架構模式。
第一種是任務線性分解架構,它是按照計算邏輯維度進行確定性拆分的并行架構設計模式。當單核處理性能存在瓶頸時,通過依賴分析,發現計算邏輯相對獨立,便可按照計算邏輯拆分成獨立的并行執行單元,從而提升性能。這種架構模式適用于業務邏輯確定性的場景,但需要注意消除或隔離數據依賴、確定執行單元工作量以及任務線性拆分擴展性差等問題。
圖片
當我們觀察圖中的左側時,可以看到計算邏輯 A、B 和 C 在同一個數據塊上進行操作。通過依賴性分析,我們可以發現 A、B 和 C 這三個計算邏輯是相對獨立的。因此,當單核處理性能達到瓶頸時,可以通過計算邏輯維度進行并行拆分來提升性能。
圖的右側展示了通過這種拆分形成的三個獨立并行執行單元。這種方式可以映射到兩個硬件線程上,從而減少處理時延。
補充說明:對于 Java 工程師來說,需要顯式地將任務映射到硬件線程的情況可能比較少;但對于嵌入式工程師來說,任務與硬件線程的映射綁定是并行設計中的一個關鍵環節。
實際上,在許多業務領域中,都存在需要根據同一個事件或數據并行觸發多個任務的場景。例如,在電商購物場景下,一筆交易成功后,系統會同時觸發多項任務,包括生成郵件通知責任人、進行多維度數據統計及更新等。
當這些觸發的業務計算邏輯相互獨立時,可以創建多個并行執行單元,分別處理拆分后的不同子任務,并根據各執行單元的工作量大小,將其與具體的硬件線程建立映射和綁定關系。
這種并行設計架構相對簡單,適用的業務場景也比較多。例如,在觀察者模式中處理類似問題、在消息隊列中解決一對多通信的業務問題等,都隱含著任務線性并發的可能性。
總的來說,任務線性分解架構適用于業務邏輯確定性的場景。在實際應用中需要注意以下幾點:
- 在并行執行單元間,通過一些手段消除或隔離數據依賴,例如使用 ThreadLocal 變量,通過數據冗余來消除依賴。
- 執行單元的工作量較為確定,便于與硬件線程建立綁定和映射關系。
- 進行并行拆分時,需要先了解全局的業務功能。任務線性拆分的擴展性相對較差。
通過這些方法,任務線性分解架構能夠在特定場景下有效提升性能。
第二種是任務分治架構,它是按照計算邏輯進行動態拆分的并行架構設計模式。在許多業務場景中,計算邏輯并非全局確定,需要根據場景判斷是否拆分成更小的子問題進行求解。在這種情況下,需要動態創建任務,并借助任務隊列來管理執行任務。這種架構模式的使用場景相對較少,但在一些實時性要求高、性能要求苛刻的場景下,如股票交易等,任務隊列以及硬件資源綁定關系通常需要單獨設計實現。
圖片
通過圖中的左側,我們可以發現,在許多業務場景中,計算邏輯并不是全局確定的。在計算過程中,有些業務需要根據具體場景來判斷是否將其拆分成更小的子問題進行求解。例如,A 計算過程中會拆分出 2 個 B 子問題,而這 2 個子問題在計算過程中又需要進一步拆分為 3 個 C 子問題來求解。
針對這種動態變化的場景進行并行設計時,不能在系統運行前完成任務的拆分,而是需要在運行時動態創建任務,并借助任務隊列來管理和執行這些任務。執行線程可以從隊列中拉取任務。在并行執行單元間,數據依賴可以通過一些手段進行消除或隔離,比如利用 ThreadLocal 變量或通過數據冗余來處理。這樣,執行單元的工作量相對確定,便于與硬件線程建立綁定和映射關系。
通常情況下,在進行并行拆分時,需要先了解全局的業務功能,盡管任務線性拆分的擴展性相對較差。這種并行設計架構模式的使用場景相對少一些。
例如,我之前基于 Akka 框架設計開發了一款智能對話引擎。在這個對話引擎系統中,用戶對話的語義信息是有限的。當收到某個用戶對話數據時,在特定上下文中,其語義可能只是全局語義中的一個較小子集。因此,我需要在這個子集中選擇語義匹配率最高的一個進行回復。
每個語義匹配率的計算邏輯與對話數據是獨立的。為了實現用戶對話消息的快速回復,我需要在特定上下文下,動態創建多個并行執行單元,分別計算語義匹配度,然后匯總選擇匹配率最高的一個。這種實現框架基于任務分治架構進行設計。
實際上,在 Java 的 java.util.concurrent.Executors 以及 Akka 等框架中,已經內置了并發任務隊列,并支持與 CPU 等硬件線程的映射,從而滿足大部分場景下的業務需求。然而,在一些實時性要求較高、性能要求非??量痰膱鼍跋?,比如股票交易,任務隊列以及硬件資源的綁定關系通常需要單獨設計實現。
1.數據幾何分解架構
數據幾何分解和任務線性分解架構風格相似,但幾何分解架構的主要特點是在不同的數據上執行相同的計算邏輯。正如圖中右側所示,拆分成不同的并行計算單元后,計算邏輯是相同的(同色表示),但數據是不同的(不同顏色表示)。
在互聯網微服務場景中,業務關鍵數據通常記錄在數據庫表中。當數據規模較大時,需要對數據庫表進行分表策略保存,這就是一種典型的數據幾何分解方式。針對這種場景,當接收到業務數據庫表查詢分析請求時,需要基于相同的計算邏輯和不同的數據庫分表組合,創建多個執行單元并行計算以提升性能。
在業務發展過程中,待處理數據規模增加是一個非常重要的變化方向,通過彈性計算資源提升業務處理能力是核心關注點之一。數據幾何分解架構是一種解決此類問題的典型方法,具有很多優點,應用非常廣泛。
最后,讓我們看看數據幾何分解架構的隱式約束條件:
- 擴展性強:采用數據幾何分解架構,其可支持的擴展性會比較強。
- 適用于 SPMD 架構:這種性能架構模式比較適合于 SPMD(Single Program Multi Data)架構。SPMD 架構使用一套相同的代碼實體并行運行在多個硬件線程上,這樣用戶只需要管理一套代碼實體即可,成本比較低。
- 獨立更新:在數據幾何分解架構中,不同并行計算單元的更新數據是獨立的。
2.遞歸數據結構
圖片
從圖中可以看到,業務處理的數據是樹狀或圖狀組織的,這表明線性幾何拆分數據會比較困難。
因此,在實際應用中,需要在遍歷過程中動態創建任務,然后逐步合并每個中間計算單元的運算結果,最終計算得到結果,如圖中右側所示。
MongoDB 是目前應用非常廣泛的開源文檔數據庫,它支持將靈活的 JSON 格式業務數據保存到數據庫中。在對業務記錄 JSON 格式內的多個字段進行數據分析時,代碼需要遞歸遍歷 JSON 中所有嵌套字段并進行分析計算。為了最大化并發執行,減少處理時延,可以采用遞歸數據架構模式,在遞歸遍歷字段過程中動態創建相應字段分析的并行執行單元。
這種架構的應用場景也相對較少,主要用于非規則結構數據的計算分析,比如樹狀結構、有向圖等數據結構。
3.數據流交互架構
圖片
從上圖中我們可以發現,這種業務場景的典型特征是計算單元的確定性較強,可以靜態規劃與硬件線程的映射關系。設計的核心是如何高效實現并發計算單元間的信息交互。
具體如何實現呢?讓我舉個例子。
在大數據領域中,ETL(Extract-Transform-Load)是一個非常典型的場景,它描述了將數據從來源端經過抽?。╡xtract)、轉換(transform)和加載(load)至目的端的過程。在這種架構模式下,計算任務單元需要動態創建,且工作量不確定。
一般來說,遞歸數據架構對應的算法是遞歸算法。在這種架構中,一個計算單元的輸出正好是另一個計算單元的輸入,消息交互是單向確定性的。同時,業務場景中還會源源不斷接收到新的輸入,需要使用相似的計算策略進行處理。
業務數據處理需求通常由多個 ETL 階段組合完成,因此在這種場景下,使用數據流交互架構會比較合適。此外,在嵌入式領域,網絡協議棧的報文處理、不同協議棧解析特定頭部字節、完成業務處理后透傳給下一層,也是使用數據流交互架構的典型場景。
在數據流交互架構中,不同并行執行單元的處理消息速率通常不一致,因此需要借助消息隊列緩存來協調。在 Java 中,各種并發的 BlockingQueue 就是這種消息隊列的一種實現方式,即典型的生產者消費者模型的處理方法。
通過這些手段,可以高效地實現并發計算單元間的信息交互,滿足動態創建任務和處理不確定工作量的需求。
4.異步交付架構
圖片
從圖上我們可以發現,該業務場景的典型特點如下:
這種系統的計算邏輯可能需要進行全局拆分,也可能無法拆分,需要根據實際情況進行處理。
讓我給你舉個例子。在微服務架構中,微服務在完成一個 REST 請求業務功能的過程中,可能需要進行多次數據庫操作,還可能需要多次調用其他微服務提供的 REST 接口。為了充分發揮性能,當我們將業務邏輯拆分為多個并行執行單元后,并行執行單元間的運行開銷差異較大時,可以使用異步交互來實現業務功能。
請注意,要想最大化地發揮這種架構的性能,還需要做到以下一點:并行執行單元能夠動態靈活地映射到特定的硬件 CPU 核上。比如,Node.js 后端業務中的 async 機制和 Java 語言中的 Future 并發機制,都是支持異步交互架構的較好語言機制。
通過這些方法,可以在微服務架構中實現高效的異步交互,提高系統性能。