轉自:InfoQ作者:Shadaj Laddad譯者:核子可樂
隨著 DeepSeek 等大模型的快速發展,人們開始意識到我們可能正處於新計算時代的開端。通用 x86 CPU 在資料中心的主導地位正加速衰退,分散式 GPU 叢集和專用加速器正在崛起,這一轉變速度甚至超出了許多人的預期。
在此背景下,GPU 程式設計的重要性日益凸顯,不少業內人士認為它將成為工程師必須掌握的核心技能。與傳統的分散式系統程式設計不同,GPU 程式設計更注重在硬體資源受限的環境下最佳化計算和圖形渲染,其開發模式與嵌入式或單機系統類似。還有觀點認為,這與 20 多年前 PlayStation 1/2 和 GBA 上的程式設計方式頗為相似。
就在最近,伯克利博士生 Shadaj Laddad 發表了一篇關於“分散式系統程式設計”的文章。在文中,他犀利指出當前的分散式程式設計模型正陷入停滯,並提出應構建以語義區域性性為優先、可被機器推理的新一代程式設計模型。Shadaj 師從研究分散式計算和機器學習的 Joe Hellerstein,具有跨學界 / 工業界背景。
而在這個技術演進節點上,這篇文章也激發了不少討論。
過去 50 年裡,行業在集中化和分散式架構之間經歷了多次反覆切換。當我們在集中化架構的極限上遇到瓶頸時,分散式架構成為了必然選擇。有激進的觀點認為,最近這次突然轉向分散式的行為“簡直就像整個行業同時集體瘋狂了一樣。”
實際上,有人分享了他上個月從做分散式系統轉向嵌入式軟體的個人經歷,表示這兩者的工作方式差異很大。比如,有人提到,他在前一份工作中,90% 以上的時間花在了排查分散式元件之間的各種故障上,那一年的程式碼量少得可憐(一年只寫了 200 行程式碼),並且經歷了嚴重的職業倦怠。

還有人表示,有些場景下,確實可以將任務轉化為一個無狀態的單機任務,既能保持同樣的執行速度,又避免了分散式計算所帶來的複雜性。

也有人認為,文章很好地闡述了分散式程式設計停滯的原因,但這並不意味著創新的空間已經消失。

分散式系統程式設計是許多人職業生涯的核心所在,從職業發展的角度來看,這篇文章為我們提供了一個全新的思考視角。你是否認同“程式設計模型停滯論”?歡迎在評論區分享您的見解與觀點。
以下是原文翻譯:
過去十年之間,我們見證了分散式系統的巨大進步,但其程式設計方式卻幾乎沒有根本性改進。雖然我們有時可以把分散式抽象出來(Spark、Redis 等),但開發人員仍需要掙扎應對併發性、容錯性和版本控制等現實挑戰。
雖然人們也在努力研究這個問題,但幾乎全部精力都集中在使用工具幫助分析如何用經典(順序)程式語言來編寫分散式系統。Jepsen 和 Antithesis 等工具已經推動了能夠驗證正確性與容錯性的最新技術,但仍無法與原生呈現基本概念的程式設計模型相提並論。以 Rust 為例,它所提供的提供記憶體安全保障就遠比 C++ 加 AddressSanitizer 的組合更為豐富。
如果在網上搜索,我們會找到大量關於編寫分散式程式碼的框架。在本文中,我們將透過探討證明這一切都只是在三大固定底層正規化之上做出的修修補補,即:external-distribution、static-location 和 arbitrary-location。換言之,我們仍缺少一種面向分散式系統的原生程式設計模型。我們將介紹這些正規化,並反思建立真正的分散式程式設計模型還需要些什麼。
External-distribution 架構代表著絕大多數“分散式”系統的表現形式。在這套模型中,軟體被編寫為順序邏輯,並針對具有順序語義的狀態管理系統執行:
-
具有分散式資料庫的無狀態服務(Aurora DSQL、Cockroach);
-
服務會使用基於 Gossip 協議的 CRDT 狀態(Ditto、ElectricSQL、Redis Enterprise 等),儘管這類系統常被宣傳為分散式系統的"銀彈",但值得注意的是,有觀點認為 CRDT 本質上只是加速了分散式事務的執行——執行其上的軟體仍然保持著順序語義,並未實現真正的分散式計算;
-
工作流與 Step Functions。
這些架構在易用性方面確實表現突出,因為它們將網路通訊、資料同步等分散式細節完全隱藏在開發者視野之外(至少在理論層面如此)。但這種抽象也帶來潛在風險:由於可序列化往往並非預設設定(快照隔離才是),因此偶爾也會出現 bug 暴露的情況。本質上,這類架構雖然構建了分散式系統,但卻不涉及任何分散式程式設計模型。
由於只需要保證選擇正確的 CRDT 一致性級別,而不再需要關注容錯或者併發錯誤,因此開發人員明顯更青睞這個選項。很明顯,它能夠將分散式的混亂隱藏在乾淨、順序的語義之下,但由此付出的則是效能和可擴充套件性的現實代價。
對一切內容進行序列化,相當於在模擬非分散式系統,只是需要昂貴的協調協議。資料庫可能在系統當中形成單點故障;因此我們要麼祈禱 us-east-1 永遠不會發生故障,要麼就得切換到像 Cockroach 這樣具有自身效能影響的多寫入器系統。雖然多數應用程式的規模都不大,完全可以容忍這樣的情況,但數量積累得多了終究會出問題。
Static-location 架構是編寫分散式程式碼的經典方式。我們會編寫多個單元——每個單元均被編寫為本地(單機)程式碼,並使用非同步網路呼叫與其他機器進行通訊:
-
服務使用 API 呼叫通訊,可能使用 async / await (gRPC, REST)。
-
Actors (Akka、Ray、Orleans)。
-
服務輪詢並推送至共享釋出 / 訂閱(Kafka)。
這些架構都為我們提供了完整的底層控制,同時也讓我們編寫出一大堆帶有網路呼叫機制的順序單機軟體。其好處在於效能和容錯性都很出色,我們可以靈活控制在何時何地執行什麼。
但各聯網單元之間的邊界卻相當僵化且不夠透明。開發人員必須就如何拆分自己的應用程式做出單向決策,決策又將對正確性產生廣泛影響;重試和訊息排序均由傳送者控制,接收者完全不知情。此外,語言和工具對單元的組成方式亦瞭解有限。Jump-to-definition 通常無法起效,而服務間也經常出現序列化不匹配的問題。
最重要的是,這種分散式系統方法從根本上消滅了語義共現與模組化的可能性。在順序程式碼中,一個個按序發生的事件會以文字形式一個個按序旋轉,函式呼叫則封裝起整個演算法。但對於 static-location 架構,開發者卻被迫在機器邊界、而非語義邊界上進行程式碼模組化。換言之,在這樣的架構中根本沒辦法將分散式演算法封裝為單個統一的語義單元。
儘管 static-location 架構為開發人員提供了對其系統底層的控制能力,但在實踐當中,如果沒有分散式系統的專業知識,穩健實現也將極其困難。實現和執行之間存在根本性的錯配:static-location 軟體是作為單機程式碼被編寫而成,但系統的正確性卻需要對整個機器叢集進行推理。因此,構建這類系統的開發團隊往往會擔心併發 bug 和故障,並留下大量因過於關鍵而無法修改觸碰的遺留程式碼。
Arbitrary-location 架構作為大多數"現代"分散式系統的基礎正規化,其核心承諾是允許開發者像編寫單機程式一樣編寫分散式程式碼。這種架構透過執行時動態排程程式碼到多臺機器執行(但即便具備遷移能力,Actor 框架仍無法徹底解決這一問題,因為開發者依然需要明確定義 Actor 的邊界,並指定訊息傳遞的發生位置):
-
分散式 SQL 引擎。
-
MapReduce 框架(Hadoop、Spark 等)。
-
流處理(Flink、Spark Straming、Storm)。
-
持久執行(Temporal、DBOS、Azure Durable Functions)。
這些架構可以優雅地處理共現問題,因為語言 /API 中沒有明確的網路邊界來拆分程式碼。但是,這種簡單性也需要付出巨大的控制性代價。由於是執行時決定著程式碼的分佈方式,我們也就無法把控應用程式的擴充套件方式、故障域的位置以及何時透過網路傳送資料。
與 external-distribution 模型一樣,arbitrary-location 架構往往會造成效能成本。持久執行系統通常會在每個步驟之間將其狀態快照至持久儲存當中。流處理系統可以動態儲存資料,並且自由地跨步驟引入非同步性。SQL 使用者完全受到查詢最佳化器的支配,最多隻能對分佈決策作出“提示”。
我們通常需要對單個邏輯的旋轉位置進行底層控制,以確保效能與正確性。比如考慮採用 Two-Phase Commit。此協議能夠將廣播提案的 leader 和確認提案的 worker 提供明確且不對稱的角色。為了正確實施這樣的協議,特准需要為這些角色明確分配特定邏輯,因為法定人數必須由單一 leader 來確定,且每個工作者必須自動決定接受或拒絕提案。因此要想在 arbitrary-location 架構中實施這樣的協議,也就必然要引入不必要的網路和協調開銷。
如果大家一直在關注“代理式”大模型領域,可能會好奇:“既然我們的軟體越來越多由大模型來編寫,那這些問題還重要嗎?”既然 static-location 模型已經足夠豐富、能夠表達所有分散式系統,幹嘛還要在乎程式設計過程是否煎熬?
在我看來,大模型反而是我們需要一種全新程式設計模型的絕佳論據。眾所周知,大模型往往很難處理上下文資訊分散在大量文字的開發場景。相反,當語義相關資訊都集中在同一位置時,大模型的表現才是最好的。
Static-location 模型迫使我們將語義連線的分散式邏輯拆分到多個模組當中。而大模型在單一機器上的正確性表現就不夠好,而編寫出多個能夠正確協同工作的單機程式更是遠遠超出其能力上限。此外,大模型仍然是在按順序做出決策,而將分散式邏輯拆分到多個聯網模組當中,對於 AI 模型的自身結構來說本就是個重大挑戰。
如果使用保留“語義區域性性”的程式設計模型,大模型倒是可以做得更好。在這樣一個假設的程式設計模型中,跨多臺機器的程式碼可以共現,從而有效解決上述問題。分散式演算法的所有相關邏輯都將互相相鄰,而大模型則可直接生成分佈邏輯。
另一個難點在於正確性。大模型會犯錯,所以我們最好是把大模型跟自動排查工作配合起來使用(Lean 就是個很好的例子。包括谷歌和 DeepSeek 在內,不少團隊長期以來一直在用它)。順序模型無法推理分散式執行可能造成哪些問題,而足夠豐富的分散式程式設計模型則可以揭示由網路延遲和故障引起的問題(類似於面向分散式系統的借用檢查器)。
儘管我們討論的每種程式設計模型都存在一定侷限性,但這也從反方向展示了分散式系統的原生程式設計模型應當支援的理想特性。那麼,我們能從這些系統中得到什麼啟示?
首先跳過 external-distribution,正如前文提到,這其實並不能算真正的分散式架構。對於能夠容忍此模式效能和語義限制的應用程式,external-distribution 當然也可以考慮。但對於通用的分散式程式設計模型,我們絕對不能接受把網路和併發都隱藏在開發者的視野之外。
Static-location 模型似乎是個不錯的起點,因為它至少能夠表達我們想要實現的所有分散式系統型別,最大的問題也只是這種程式設計模型在分佈推理方面提供不了多少幫助。具體來講,這裡缺失了 arbitrary-location 模型所能提供的兩大要素:
-
在單一函式中編寫跨多臺相鄰機器的邏輯;
-
跨網路邊界展示分散式行為的語義資訊,例如訊息重新排序、重試和序列化格式。
但在這兩點背後,又對應著 static-location 相較於 arbitrary-location 的兩大重要優勢:
-
明確控制機器上邏輯的放置,並能夠執行本地原子計算;
-
豐富的容錯保證和網路語義選項,同時保證語言不會將我們鎖定在全域性協調和恢復協議當中。
總之,現在是時候打造一套真正的原生程式設計模型了——比如說 Rust 版本的分散式系統開發模型。也只有這樣,我們才能解決一切難題、縱享一切優勢。
參考連結:
https://www.shadaj.me/writing/distributed-programming-stalled
https://news.ycombinator.com/item?id=43195702