BEAM和JVM虛擬機器對比:JVM是為並行而構建的,而BEAM是為併發構建的 | Erlang

banq發表於2020-05-15

任何程式語言在Erlang生態系統中的成功都可以分為三個緊密耦合的元件。它們是:

  1. Erlang程式語言的語義,並在其上實現其他語言
  2. 用於構建可伸縮和彈性併發系統的OTP庫和中介軟體
  3. 與語言語義緊密耦合的BEAM虛擬機器和OTP。

單獨使用這些元件中的任何一個,您將獲得亞軍。但是,將這三個因素放在一起,您將獲得可伸縮,靈活的軟實時系統的無可爭議的贏家。引用Joe Armstrong的話:“您可以複製Erlang庫,但是如果它不能在BEAM上執行,則無法模擬語義”。

在本文中,我們想探索BEAM VM內部。我們將在適用的情況下將它們與JVM進行比較和對比,強調您為什麼要注意它們並加以注意。長期以來,此元件一直被視為黑匣子,並且在不瞭解原因或含義的情況下被視為理所當然。現在該改變這種情況了!

BEAM的亮點

發明Erlang和BEAM VM是解決特定問題的正確工具。它們是由愛立信開發的,旨在幫助實現處理移動和固定網路的電信基礎設施。該基礎架構本質上是高度併發和可伸縮的。它必須顯示軟實時屬性,並且永遠不會失敗。BEAM VM通過提供可在可預測的併發程式設計模型之上執行的微調功能進行了優化,以解決許多挑戰。

它的祕訣是輕量級程式,它們不共享記憶體,由排程程式管理,該排程程式可以跨多個核心管理數百萬個程式。它使用基於每個程式執行的垃圾收集器,並對其進行了高度優化以減少對其他程式的影響。結果,垃圾收集器不會影響系統的整體軟實時屬性。BEAM也是唯一使用規模廣泛且具有內建分發模型的VM,它具有內建的分發模型,該模型允許程式透明地在多臺計算機上執行。

JVM的亮點

Java虛擬機器(JVM)是​​由Sun Microsystem開發的,旨在提供一個可在任何地方執行的“一次編寫”程式碼的平臺。他們建立了一種類似於C ++的物件導向的語言,但是記憶體安全,因為其執行時錯誤檢測會檢查陣列範圍和指標取消引用。在Internet時代,JVM生態系統變得非常流行,使其成為企業伺服器應用程式的實際標準。滿足廣泛用例的虛擬機器以及可滿足企業發展需求的令人印象深刻的庫集,使廣泛的適用性成為可能。

JVM設計時考慮了效率。它的大多數概念是流行作業系統中功能的抽象,例如對映到作業系統執行緒的執行緒模型。JVM是高度可定製的,包括垃圾收集器(GC)和類載入器。一些最先進的GC實現提供高度可調整的功能,以適應基於共享記憶體的程式設計模型。JVM允許您在程式執行時更改程式碼。而且,JIT編譯器允許將位元組碼編譯為本機程式碼,目的是加快應用程式的各個部分。

Java世界中的併發性主要與在並行執行緒中執行應用程式有關,以確保它們是快速的。由於併發原語的共享記憶體模型帶來了挑戰,因此使用併發原語進行程式設計是一項艱鉅的任務。為了克服這些困難,人們嘗試簡化和統一併發程式設計模型,最成功的嘗試是Akka框架

併發與並行

如果部分程式碼在多個核心,處理器或計算機上同時執行,則我們談論並行程式碼執行,而併發程式設計是指獨立處理到達系統的事件。可以在單執行緒硬體上模擬併發執行,而並行執行則不能。儘管這種區別似乎很古怪,但這種差異導致需要解決的問題非常不同。

想想很多廚師在做一盤Carbonara義大利麵。在並行方法中,將任務分配給可用廚師的數量,並且只要完成這些廚師完成其特定任務的速度,就可以完成單個部分。在一個併發的世界中,每位廚師將獲得一部分,每位廚師將完成所有任務。您將並行性用於速度,併發性用於規模

並行執行試圖將問題的最佳分解解決為彼此獨立的部分。將水煮沸,煮義大利麵,混合雞蛋,炸瓜裡阿塞火腿,將佩克立諾乳酪磨碎。共享資料(或在我們的示例中為餐盤)由鎖,互斥鎖和各種其他技術處理,以確保正確性。另一種看待這種情況的方式是資料本身(烹飪原料)是首先存在那裡,並且我們希望利用盡可能多的並行CPU資源來儘快完成工作。(分配更多廚師做每個獨立部分,儘快完成工作)

另一方面,並​​發程式設計處理許多事件,這些事件在不同的時間到達系統,並嘗試在合理的時間內處理所有事件。在多核或分散式體系結構上,某些執行是並行執行的,但這不是必需的。另一種看待它的方法是,同一位廚師按照始終相同的順序演算法,將水煮沸,煮義大利麵,混合雞蛋等是一個烹飪流程。跨這個烹飪流程的變化是要處理的資料本身(烹飪原料),這些資料(烹飪原料)存在於多個例項(多個廚師烹飪流程)中。

JVM是為並行而構建的,而BEAM是為併發構建的。它們是兩個本質上不同的問題,需要不同的解決方案。

BEAM和併發

BEAM提供輕量級流程為正在執行的程式碼提供上下文。這些程式也稱為參與者,不共享記憶體,而是通過訊息傳遞進行通訊,將資料從一個程式複製到另一個程式。訊息傳遞是虛擬機器通過各個程式擁有的郵箱實現的功能。訊息傳遞是一種非阻塞操作,這意味著將訊息傳送到另一個程式幾乎是即時的,並且不會阻塞傳送者的執行。傳送的訊息採用不可變資料的形式,從傳送過程的堆疊複製到接收者的郵箱。無需在程式之間使用鎖和互斥鎖即可實現此目的,而在多個程式並行將訊息傳送到同一收件人的情況下,只需對郵箱進行鎖定即可。

不變的資料和訊息傳遞使程式設計師能夠編寫彼此獨立工作的流程,並專注於功能而不是記憶體的低階管理和任務排程。事實證明,這種簡單的設計不僅適用於單個執行緒,而且適用於在同一VM中執行的本地計算機上的多個執行緒,並使用內建的分發,通過具有VM和計算機群集的網路在內部執行。如果訊息在程式之間是不可變的,則可以不加鎖地將它們傳送到另一個執行緒(或計算機),從而在分散式多核體系結構上幾乎線性地擴充套件。在本地VM上與在VM群集中以相同的方式處理程式,無論接收程式的位置如何,訊息傳送都是透明的。

程式不共享記憶體,因此您可以複製資料以恢復彈性並分發資料以實現規模擴充套件。這意味著在兩個不同的機器上具有相同程式的兩個例項,彼此之間共享狀態更新。如果一臺計算機發生故障,則另一臺計算機具有資料副本,並且可以繼續處理該請求,從而使系統具有容錯能力。如果兩臺計算機都可執行,則兩個程式都可以處理請求,從而為您提供可伸縮性。BEAM提供了高度優化的原語,使所有這些無縫地工作,而OTP(“標準庫”)提供了更高階別的結構,以簡化程式設計師的生活。

Akka在複製更高階別的結構方面做得很好,但是由於缺少JVM提供的原語而在一定程度上受到了限制,從而使其可以高度優化併發性。儘管JVM的原語支援更廣泛的用例,但由於它們沒有用於通訊的內建原語且通常基於共享記憶體模型,因此它們使對分散式系統的程式設計變得更加困難。例如,您在分散式系統中的何處放置共享記憶體?以及訪問它的成本是多少?

排程器

我們提到過,BEAM的最強功能之一就是能夠將程式分解為小的,輕量級的過程。管理這些過程是排程程式的任務。與JVM將其執行緒對映到OS執行緒並讓作業系統排程它們不同,BEAM帶有自己的排程程式。

預設情況下,排程程式為每個核心啟動一個OS執行緒,並優化它們之間的工作負載。每個過程都包含要執行的程式碼和隨時間變化的狀態。排程程式會選擇執行佇列中準備執行的第一個程式,併為其執行一定程度的縮減,其中每次縮減都大致等同於命令。一旦程式用盡了減少量,被I / O阻塞,正在等待訊息或完成執行其程式碼,排程程式就會在執行佇列中選擇下一個程式並進行排程。這種排程技術稱為搶先式。

我們多次提到Akka框架,因為它的最大缺點是需要用排程點註釋程式碼,因為排程不是在JVM級別進行的。通過從程式設計師手中除去控制元件,可以保留和保證軟實時屬性,因為不存在它們意外導致程式匱乏的風險。

程式可以散佈在可用的排程程式執行緒周圍,並最大程度地利用CPU。有許多方法可以調整排程程式,但是它很少見,僅在邊緣和邊界情況下才需要,因為預設選項涵蓋了大多數使用模式。

關於排程程式,經常出現一個敏感的話題:如何處理本機實現的函式(NIF)。NIF是用C編寫的程式碼片段,被編譯為庫並在與BEAM相同的記憶體空間中執行以提高速度。NIF的問題在於它們不是搶佔式的,並且會影響排程程式。在最新的BEAM版本中,新增了一項新功能,即髒排程程式,以更好地控制NIF。骯髒的排程程式是在不同執行緒中執行的單獨的排程程式,以最大程度地減少NIF對系統造成的中斷。髒這個詞是指這些排程程式執行的程式碼的性質。

垃圾收集器

BEAM的特點是,僅在需要時才在每個程式的基礎上執行垃圾回收,而不會影響在執行佇列中等待的其他程式。結果,Erlang中的垃圾收集不會“停止世界”。它可以防止處理延遲高峰,因為VM不會從整體上停止-僅特定程式停止,並且絕不會同時停止所有程式。

熱程式碼載入

熱程式碼載入可能是BEAM引用最多的獨特功能。熱程式碼載入意味著可以通過更改系統中的可執行程式碼來更新應用程式邏輯,同時保留內部流程狀態。這是通過替換已載入的BEAM檔案並指示VM替換正在執行的程式中的程式碼引用來實現的。

對於電信基礎架構無需停機程式碼升級而言,這是一項至關重要的功能,在該基礎架構中,冗餘硬體用於處理高峰流量。開發人員可以通過替換部分程式碼來加快迭代速度,而不必重新啟動系統來對其進行測試。即使該應用程式並非設計為可在生產環境中進行升級,也可以減少重新編譯和重新部署所需的時間。

何時不使用BEAM

速度從來都不是Beam優先考慮事項,電信基礎設施不需要快速執行,數字運算最好留給JVM,Go或其他編譯成本地語言的語言使用。毫不奇怪,在JVM上執行的Erlang版本Erjang上的浮點運算比BEAM快5000%。但是我們看到BEAM大放異彩的地方是利用它的併發來安排管理數字運算,將具體髒活外包給C,Julia,Python或Rust。

對於具有更多處理能力的嵌入式系統(多核已成為常態),您需要併發性,而BEAM令人眼前一亮。上世紀90年代,我們實現了電話交換機,以處理執行在具有16mb記憶體的嵌入式板上的成千上萬的使用者。

在諸如GRiSP之類的裸機上執行的Erlang VM的實現將為您提供類似的保證。

 

相關文章