Ruby 3.1初次加入程序內JIT編譯器YJIT,經過一年的時間,YJIT成為Ruby 3.2中的正式功能,Shopify R&RI(Ruby & Rails Infrastructure)團隊則是該功能的主要推手,開發團隊現在詳細說明YJIT的開發細節以及實質帶來的改進

由於Shopify的所有重要基礎設施,包括商店以及網站的底層,都仰賴Ruby和Ruby on Rails,Shopify的大型伺服器叢集散布全球,每分鐘能夠處理超過7,500萬次請求,在2020年的時候,Shopify決定要開發一個相對簡單的JIT編譯器YJIT,目標是以YJIT提高店面渲染器(Storefront Renderer,SFR)的效率。

這原本只是Shopify在2020年啟動的內部專案,但是受到CRuby核心貢獻者的邀請,YJIT進入Ruby並且在3.1版本中作為實驗性功能發布。YJIT編譯器到了Ruby 3.2成為正式版本,比起Ruby 3.1的實驗性版本,不只更強健也更易於維護,記憶體效率與執行效能的提升,使得YJIT 3.2更適合用於生產環境中。

Shopify在開發YJIT 3.2時,決定要將YJIT從C99移植到Rust上,開發團隊提到這項決定的主要考量有兩個,第一個是Rust提供了C所沒有的安全性,開發團隊認為,這對於具有許多約束的低階系統程式開發來說非常重要。

第二個原因則是Rust的程式碼更容易維護。使用C編寫YJIT程式碼時,開發團隊參考C巨集實作動態陣列,他們認為這樣的方法既不安全也不夠聰明,而Rust擁有豐富的標準函式庫和好用的抽象,因此他們只花3個月就將YJIT移植到Rust,還使整體程式碼庫更容易維護。

Ruby 3.2中的YJIT記憶體使用更有效率,開發團隊解釋,JIT編譯器的缺點之一,便是需要使用比直譯器更多的記憶體,因為JIT編譯器需要生成可執行機器碼,但是直譯器不需要,而且JIT編譯器還需要分配記憶體給後設資料,因此也增加許多記憶體開銷。

YJIT 3.1編譯器大量的記憶體開銷,使得Shopify在生產部署上面臨挑戰,因此開發團隊進行多項改進大幅降低記憶體用量,除了最佳化後設資料占用的記憶體,並且對不再使用的機器碼實作垃圾回收,因此在Ruby 3.2中,YJIT記憶體開銷已降到Ruby 3.1的三分之一,YJIT只會懶散地替機器碼分配記憶體分頁,而不會預先分配並且初始化一大塊記憶體,這項改進讓YJIT更適合用於生產中。

YJIT 3.2改進還包括執行速度,不只使用更少的記憶體,而且在Railsbench基準測試上,執行速度比Ruby 3.2的直譯器快38%,與Ruby 3.1.3的直譯器相比更是快了57%。

YJIT 3.2因為有一個新的後端,可以對多種CPU產生機器碼,因此新增支援了ARM64 CPU。之前在Ruby 3.1中的YJIT只支援Mac和Linux上的x86-64,但因為Shopify開發人員開始採用Apple M1/M2筆電,導致他們自己只能使用Rosetta模擬執行YJIT,這驅使他們開始提供ARM64 CPU的支援,使得YJIT可以在Apple M1/M2、AWS Graviton以及樹莓派上執行,而且比起英特爾的x86-64 CPU,YJIT在Mac M1硬體上獲得大幅加速。

因為YJIT 3.1記憶體開銷未經最佳化,因此只標記為實驗性,Shopify也未將其部署到全球,但透過部分SFR節點部署收集統計資料,觀察到基準測試中無法發現的效能問題,而到了Ruby 3.2,YJIT已經獲得足夠改進,現在Shopify已經著手將YJIT 3.2部署到全球SFR基礎設施,獲得5%到10%的實際加速。

開發團隊提到,目前YJIT最大的缺點,仍然是記憶體占用,開發團隊還會繼續減少記憶體占用,同時他們也將改進Ruby的執行速度,開發團隊解釋,Ruby中有許多方法呼叫,迴圈迭代和大部分的基本操作都是方法呼叫,典型的Ruby程式碼存在許多小方法呼叫,因此他們試圖會從方法呼叫加速下手,改進Ruby程式執行速度。

熱門新聞

Advertisement