跳至主要內容
蒼時弦也
蒼時弦也
資深軟體工程師
發表於

Kobako:Cold Start 原來能快 100 倍?

這篇文章是 Kobako 系列的一部分。

Kobako 是近期我針對 Ruby 生態系中,對 Harness Engineering 支援所開發基於 WebAssembly 和 mruby 的沙盒(Sandbox)用來填補 AI 所撰寫的程式碼,沒有可以安全運行的環境。

關於 Kobako 的設計理念,我在 上一篇文章 已經介紹過,這次想聊的是效能。在初期的版本中 Cold Start(冷啟動,通常指初次啟動)大概要花上 500 ms 左右,這遠比最佳實踐通常會抓 200 ms 回應來說慢的不少,即使 AI 通常接受更慢的回應,但這不是等待 LLM 回應,仍該用過去的 API 標準來看待。

預先編譯

WebAssembly 經過多年發展後,已經有很多新的技術跟規格,其中 cwasm(Wasmtime 把 .wasm 預先編譯後的產物)就是一種 AOT(Ahead-of-Time,預先編譯)的處理方式。

原因在於,我們編譯為 wasm 格式後,是針對 WebAssembly 的虛擬機器所設計,如果拿來跑 mruby 大致上就是在一個 WebAssembly 虛擬機器中,再啟動一個 mruby 虛擬機器,套了兩層在運作。

透過 cwasm 處理,會先在運行的環境直接編譯成當下環境 CPU 對應的機器碼,那就無限接近於把 WebAssembly 虛擬機器消除,只留下 mruby 虛擬機器的成本,啟動速度就從 500 ms 縮減到 5 ms 左右,因此 Cold Start 的時間馬上快上 100 倍。

簡單來說,Kobako 本身並沒有做太多處理,只是很單純的透過 WebAssembly 的標準,就極大的加速到跟一次簡單的 SQL 查詢差不多,對大部分應用幾乎等同無感。

今年 RubyKaigi 中 Matz 的新專案 Spinel 正好就是一種 Ruby 的 AOT 編譯器,至於 cwasm 我則是第一次接觸到,畢竟過往都還停留在 wasm 的認識,這次跟 AI 協作意外學到新的概念。

預先偷跑

當在裝置上啟動過一次 Kobako 後,後續的 Cold Start 就會因為 AOT 從 500 ms 變成只要 5 ms 就能啟動完畢,那麼 Kobako::Sandbox.new 也能變快嗎?

原本的設計上,我們會把每個 Kobako::Sandbox.new 都獨立建立一個實例(Instance)並且在每次 #run 或者 #eval 時重新初始化 mruby 虛擬機器,確保可重用與乾淨隔離,但每多一個沙盒就會多佔用約 560 KB 的記憶體,對於希望「大量產生、刪除」的設計不太友善,當數量增加記憶體就會急劇消耗,而且還有一個初始化的時間約 130 µs 的成本在,即使這已經非常快。

Wasmtime 提供了一個叫做 wizer 的機制,我們可以預先把運行的記憶體準備好。這是因為每次啟動新的 mruby 虛擬機器時,對所有使用相同 .wasmKobako::Sandbox 是完全相同的記憶體,那麼預先把把 mruby 虛擬機器跑到啟動好的狀態存到 .wasm 中,未來啟動時就不需要再次去計算。

接下來搭配 Wasmtime 的 InstancePreCopy-on-Write 兩個設計,就可以把啟動時間跟記憶體使用壓的更低。

  • InstancePre - 把 Kobako 的 ABI(Application Binary Interface,應用程式二進位介面)先計算好,未來重新利用,不用每次都計算
  • Copy-on-Write - 建立新的 Kobako::Sandbox 時,使用同一份 Baked 的 mruby 虛擬機器,不另外建立

透過這些處理,做一次 Kobako::Sandbox.new 的時間,就從 130 µs 降低到 30 µs 左右,記憶體使用從 560 KB 減少到 1 KB,因為不再需要每個沙盒都保存完整的記憶體,在有改變之前都會使用同一份基礎。

設計取捨

原本在我的預期中,會透過消耗相對多的記憶體去換取更快的速度,但結果反而出乎我的預期,在速度跟記憶體使用上都得到很大的提升,更快地幫助我把專案推進到可以正式上線的等級。

尤其是這次用到的技術,很多都不是新技術,而是建構在已經被使用多年的機制。但透過 AI 的輔助,我很快的挖掘出這些技術,並且放置到恰當的位置。

這對軟體工程師這個職業的影響,依舊還是對應我們所熟知的衝擊,有經驗的工程師更有價值,原因就是「無條件接受」跟「有意識篩選」是兩件事情,後者是專業工程師的素養,在這次改版之前,我已經直覺的「可以用 Cache 重用」這樣的直覺判斷,先減少「每次都要等 500 ms」變成只等一次。

然而當整體功能完善後,考慮到每次啟動的時間跟佔用的記憶體,就讓我開始思考「還能更好嗎?」但受限於對 WebAssembly 技術不是熟悉的情境,透過 AI 大量搜集資料、探索,很快就確認 AOT、Bake 等技術是合理且有幫助的,分段處理(如果 AOT 不成立,做 Bake 大概也沒差異太大)一步一步推進來達到更好的成果。

我認為這在 AI 時代是很值得思考的問題,我們從 AI 身上拿到的「加速」是什麼?以 Kobako 的案例,整個專案極大的提升背後,是快速搜集資料,以及對軟體開發知識的熟悉得出適當的判斷。