經過Mozilla的改進,從Firefox 103版本開始,macOS上的Firefox更順暢,回應能力明顯提高,特別是用戶開啟許多頁籤,或是電腦忙於執行其他應用程式的時候,Firefox現在能更即時地回應用戶的操作,而官方解釋,這是透過調整記憶體分配器中鎖實做的方法達成的。

Firefox在所有架構中使用高度自定義的jemalloc記憶體分配器版本,以達到Firefox最佳效能和記憶體使用,記憶體分配器必須為執行緒安全,而且為了提高效能,還需要為來自不同執行緒的大量平行請求提供服務,為了實現這件事,jemalloc在內部結構使用鎖。

分配器中鎖實作方法和程式碼庫的其餘部分都不同,具體來說,創建和使用互斥鎖不能發出新的記憶體分配要求,因為這會導致分配器本身無限遞迴,所以分配器傾向使用底層作業系統的原生Thin Lock,而在macOS中,Firefox一直都是使用OSSpinLock的鎖實作。

而OSSpinLock自旋鎖(Spin Lock)並非一般的互斥鎖,互斥鎖在被一個執行緒占用時,則使另一個試圖取得鎖的執行緒進入睡眠狀態,自旋鎖則是當執行緒要鎖定一個已經被鎖定的OSSpinLock實例,該執行緒會一直忙於輪詢該鎖,而非睡眠等待鎖釋放,這個行為被稱為自旋。

Mozilla解釋,雖然自旋會消耗CPU周期和電力,但是讓執行緒睡眠卻會對效能產生更重大的影響,要將睡眠中的執行緒喚醒,需要兩次上下文切換,經歷將執行緒狀態存入記憶體和恢復的過程,而且在執行緒恢復時,可能會在不同核心上執行,該核心可能充滿不相關的資料。這些因素會讓執行緒採用互斥鎖比自旋鎖速度更慢。

所以當執行緒試圖獲得的鎖僅會鎖定很短暫的時間,那讓執行緒短暫自旋可能是相對有利的,付出的成本也會低於睡眠。但自旋鎖缺點便是自旋太久會造成運算資源浪費,特別是當電腦負載本來就很重的時候,可能會降低已經擁有鎖的執行緒運作速度,進而造成更多執行緒需要鎖的機會,導致許多執行緒不停自旋。

過去Firefox在macOS上使用的OSSpinLock,在負載輕的系統提供非常好的效能,但是在負載增加時表現不佳,其具有兩個最根本的缺陷,就是OSSpinLock會在使用者空間中自旋,並且從來不睡眠。

使用者空間不會知道系統正執行多少負載,因此如果是在核心空間,那鎖就可以做出更加明智的決定,也就是當負載很高的時候便不自旋,官方提到,在執行緒無法獲得鎖的時候,不睡眠會使狀況更糟,特別是核心不知道存在等待鎖的執行緒,又另外喚醒正在爭奪同一個鎖的執行緒,產生更多的自旋和競爭,最終可能使Firefox當掉。

Apple因為OSSpinLock的問題決定棄用該鎖實作,取而代之的是官方替代實作os_unfair_lock,但在Mozilla使用os_unfair_lock並進行自動化測試後,發現部分效能下降30%,而這個原因正是在os_unfair_lock中,執行緒不會因為爭用而自旋,反而立即進入睡眠,這使記憶體分配器效能更差。

Mozilla進一步發現Apple函式庫有os_unfair_lock_with_options()自旋鎖可供使用,由於能夠於核心空間執行自旋,因此也就可以在適合的時機執行載入和調度。在使用新的自旋鎖後,Firefox在輕負載系統上的效能與OSSpinLock大致相同,在負載較重的系統能夠提供更好的回應,並且降低自旋造成的電力浪費。

熱門新聞

Advertisement