效能基礎

效能意味著效率。在開放網路應用的背景下,本文件總體解釋了什麼是效能,瀏覽器平臺如何幫助提升效能,以及你可以使用哪些工具和流程來測試和提升效能。

什麼是效能?

最終,使用者感知的效能是唯一重要的效能。使用者透過觸控、移動和語音向系統提供輸入。作為回報,他們透過視覺、觸覺和聽覺感知輸出。效能是系統輸出響應使用者輸入的質量。

在其他條件相同的情況下,如果程式碼最佳化的目標不是使用者感知效能(以下簡稱 UPP),那麼它在與為 UPP 最佳化的程式碼競爭時就會失敗。使用者更喜歡一個響應迅速、流暢的應用,即使它每秒只處理 1,000 個數據庫事務,而不是一個卡頓、無響應的應用,即使它每秒處理 100,000,000 個事務。當然,最佳化其他指標並非毫無意義,但真正的 UPP 目標是第一位的。

接下來的幾個小節將指出並討論重要的效能指標。

響應性

響應性是指系統響應使用者輸入(可能包含多個輸出)的速度。例如,當用戶點選螢幕時,他們期望畫素以某種方式改變。對於這種互動,響應性指標是點選與畫素改變之間經過的時間。

響應性有時涉及多個反饋階段。應用啟動是下面將詳細討論的一個特別重要的案例。

響應性很重要,因為當人們被忽視時,他們會感到沮喪和憤怒。每當你的應用未能響應使用者輸入時,它都在忽視使用者。

幀速率

幀率是系統向用戶顯示畫素的速度。這是一個熟悉的慨念:每個人都更喜歡每秒顯示 60 幀的遊戲,而不是每秒顯示 10 幀的遊戲,即使他們無法解釋原因。

幀率作為一種“服務質量”指標很重要。電腦顯示器旨在透過向用戶眼睛傳遞模仿現實的光子來“欺騙”使用者的眼睛。例如,印有文字的紙張以某種模式將光子反射到使用者的眼睛。透過操縱畫素,閱讀器應用以類似的模式發射光子來“欺騙”使用者的眼睛。

正如你的大腦推斷的那樣,運動不是跳躍和離散的,而是平滑和連續地“更新”。(頻閃燈很有趣,因為它們顛覆了這一點,透過剝奪你的大腦輸入來創造離散現實的錯覺)。在電腦顯示器上,更高的幀率可以更真實地模仿現實。

注意:人類通常無法感知 60Hz 以上幀率的差異。這就是為什麼大多數現代電子顯示器都設計成以這種速率重新整理。例如,電視機對蜂鳥來說可能看起來卡頓且不真實。

記憶體使用

記憶體使用是另一個關鍵指標。與響應性和幀率不同,使用者不會直接感知記憶體使用,但記憶體使用與“使用者狀態”緊密相關。一個理想的系統會始終維護 100% 的使用者狀態:系統中的所有應用程式都會同時執行,並且所有應用程式都會保留使用者上次與應用程式互動時建立的狀態(應用程式狀態儲存在計算機記憶體中,這就是為什麼近似值很接近)。

由此得出重要但反直覺的推論:設計良好的系統不會最大化可用記憶體量。記憶體是一種資源,可用記憶體是未使用的資源。相反,設計良好的系統已經過最佳化,以儘可能多地使用記憶體來維護使用者狀態,同時滿足其他 UPP 目標。

這並不意味著系統應該浪費記憶體。當系統使用超過維護某個特定使用者狀態所需的記憶體時,系統正在浪費可以用來保留其他使用者狀態的資源。實際上,沒有系統能夠維護所有使用者狀態。智慧地將記憶體分配給使用者狀態是一個重要的問題,我們將在下面更詳細地討論。

電量消耗

這裡討論的最後一個指標是電量消耗。與記憶體使用一樣,使用者只能透過裝置能保持所有其他 UPP 目標的時間長短來間接感知電量消耗。為了滿足 UPP 目標,系統必須只使用所需的最小電量。

本文件的其餘部分將根據這些指標討論效能。

平臺效能最佳化

本節簡要概述了 Firefox/Gecko 如何在所有應用之下,普遍性地為效能做出貢獻。從開發人員或使用者的角度來看,這回答了“平臺為您做了什麼?”的問題。

Web 技術

Web 平臺提供了許多工具,有些比其他工具更適合特定任務。所有應用邏輯都用 JavaScript 編寫。為了顯示圖形,開發人員可以使用 HTML 或 CSS(即,高階宣告性語言),或者使用由 <canvas> 元素(包括 WebGL)提供的低階命令式介面。HTML/CSS 和 Canvas 之間“中間”是 SVG,它兼具兩者的優點。

HTML 和 CSS 極大地提高了生產力,有時犧牲了幀率或畫素級渲染控制。文字和影像自動重排,UI 元素自動接收系統主題,並且系統為開發人員最初可能不會想到的某些用例提供“內建”支援,例如不同解析度的顯示器或從右到左的語言。

canvas 元素直接為開發人員提供畫素緩衝區進行繪製。這使開發人員可以畫素級控制渲染和精確控制幀率,但現在開發人員需要處理多種解析度和方向、從右到左的語言等等。開發人員使用熟悉的 2D 繪圖 API 或 WebGL(一種“接近硬體”的繫結,主要遵循 OpenGL ES 2.0)繪製到 canvas 上。

Gecko 渲染

Gecko 的 JavaScript 引擎支援即時(JIT)編譯。這使得應用程式邏輯的效能可與 Java 虛擬機器等其他虛擬機器相媲美,在某些情況下甚至接近“原生程式碼”。

Gecko 中支撐 HTML、CSS 和 Canvas 的圖形管道透過多種方式進行了最佳化。Gecko 中的 HTML/CSS 佈局和圖形程式碼減少了滾動等常見情況的無效和重繪;開發人員“免費”獲得此支援。Gecko“自動”繪製的畫素緩衝區和應用程式“手動”繪製到 canvas 中的畫素緩衝區在繪製到顯示幀緩衝區時最大程度地減少了複製。這是透過避免中間表面(如果它們會產生開銷,例如許多其他作業系統中每個應用程式的“後臺緩衝區”)以及使用可由合成器硬體直接訪問的圖形緩衝區專用記憶體來實現的。複雜場景使用裝置的 GPU 進行渲染以實現最大效能。為了改善功耗,簡單場景使用特殊的專用合成硬體進行渲染,而 GPU 則處於空閒或關閉狀態。

對於富應用而言,完全靜態內容是例外而非規則。富應用使用具有 animationtransition 效果的動態內容。過渡和動畫對應用程式特別重要:開發人員可以使用 CSS 以簡單的高階語法聲明覆雜的行為。反過來,Gecko 的圖形管道經過高度最佳化,可以高效渲染常見動畫。常見動畫會“解除安裝”到系統合成器,系統合成器可以以高效能、高能效的方式渲染它們。

應用的啟動效能與執行時效能同樣重要。Gecko 經過最佳化,可以高效載入各種內容:整個 Web!多年來針對此內容進行的改進,例如並行 HTML 解析、智慧排程重排和影像解碼、巧妙的佈局演算法等,同樣適用於改進 Firefox 上的 Web 應用。

應用效能

本節旨在回答開發人員提出的問題:“如何讓我的應用更快?”

啟動效能

一般而言,應用程式啟動會經歷三個使用者感知的事件:

  • 第一個是應用程式的首次繪製——足以繪製初始幀的應用程式資源已載入完畢的時間點
  • 第二個是應用程式變得可互動——例如,使用者能夠點選按鈕並且應用程式做出響應
  • 最後一個事件是完全載入——例如,音樂播放器中列出了使用者的所有專輯

快速啟動的關鍵是牢記兩點:UPP(使用者感知效能)是唯一重要的,並且每個上述使用者感知事件都存在一個“關鍵路徑”。關鍵路徑正是並且僅僅是必須執行才能產生該事件的程式碼。

例如,要繪製應用程式的第一個框架,該框架在視覺上包含一些 HTML 和用於樣式化該 HTML 的 CSS

  1. HTML 必須被解析
  2. 必須構建該 HTML 的 DOM
  3. 該 DOM 部分中的影像等資源必須被載入和解碼
  4. CSS 樣式必須應用於該 DOM
  5. 樣式化的文件必須重排

此列表中沒有“載入不常用選單所需的 JS 檔案”;“獲取並解碼高分列表的影像”等。這些工作項不在繪製第一幀的關鍵路徑上。

這似乎是顯而易見的,但為了更快地達到使用者感知的啟動事件,主要的“訣竅”是只執行關鍵路徑上的程式碼。透過簡化場景來縮短關鍵路徑。

Web 平臺具有高度動態性。JavaScript 是一種動態型別語言,Web 平臺允許動態載入程式碼、HTML、CSS、影像和其他資源。這些功能可用於透過在啟動後“惰性”載入不必要的內容來延遲非關鍵路徑上的工作。

另一個可能延遲啟動的問題是空閒時間,這是由於等待請求響應(如資料庫載入)引起的。為了避免這個問題,應用程式應該在啟動時儘早發出請求(這稱為“預載入”)。這樣,當稍後需要資料時,它有望已經可用,並且應用程式不必等待。

注意:有關改進啟動效能的更多資訊,請閱讀 最佳化啟動效能

同樣,請注意,本地快取的靜態資源載入速度遠快於透過高延遲、低頻寬行動網路獲取的動態資料。網路請求絕不應出現在早期應用程式啟動的關鍵路徑上。可以透過 Service Workers 實現本地快取/離線應用。請參閱 離線和後臺操作 以獲取有關使用 Service Workers 實現離線和後臺同步功能的指南。

幀速率

高幀率的首要條件是選擇正確的工具。使用 HTML 和 CSS 來實現主要是靜態的、可滾動的且不經常動畫化的內容。使用 Canvas 來實現高度動態的內容,例如需要嚴格控制渲染且不需要主題的遊戲。

對於使用 Canvas 繪製的內容,由開發人員來達到幀率目標:他們可以直接控制繪製的內容。

對於 HTML 和 CSS 內容,實現高幀率的方法是使用正確的原語。Firefox 經過高度最佳化以滾動任意內容;這通常不是一個問題。但通常為了速度而犧牲一些通用性和質量,例如使用靜態渲染而不是 CSS 徑向漸變,可以將滾動幀率推高到目標之上。CSS 媒體查詢允許將這些折衷限制在需要它們的裝置上。

許多應用程式透過“頁面”或“面板”使用過渡或動畫。例如,使用者點選“設定”按鈕以過渡到應用程式配置螢幕,或者設定選單“彈出”。Firefox 經過高度最佳化,可以對以下場景進行過渡和動畫:

  • 使用大小近似於裝置螢幕或更小的頁面/面板
  • 過渡/動畫 CSS transformopacity 屬性

遵守這些準則的過渡和動畫可以解除安裝到系統合成器,並以最高效率執行。

記憶體和電量消耗

改進記憶體和電量消耗與加速啟動是類似的問題:不要做不必要的工作或延遲載入不常用的 UI 資源。要使用高效的資料結構並確保影像等資源得到良好最佳化。

現代 CPU 在大部分空閒時可以進入低功耗模式。不斷觸發計時器或保持不必要的動畫執行的應用程式會阻止 CPU 進入低功耗模式。節能應用程式不應該這樣做。

當應用程式被髮送到後臺時,會在其文件上觸發一個 visibilitychange 事件。這個事件是開發者的朋友;應用程式應該監聽它。

應用效能的具體編碼技巧

以下實用技巧將有助於改善上述一個或多個應用程式效能因素。

使用 CSS 動畫和過渡

不要使用某個庫的 animate() 函式(它可能當前使用許多效能不佳的技術,例如 setTimeout()top/left 定位),而是使用 CSS 動畫。在許多情況下,你實際上可以使用 CSS 過渡來完成工作。這之所以有效,是因為瀏覽器被設計用來最佳化這些效果,並使用 GPU 平滑地處理它們,從而最大限度地減少對處理器效能的影響。另一個好處是,你可以使用標準化的語法在 CSS 中定義這些效果以及應用程式的其餘外觀。

CSS 動畫讓你可以使用 關鍵幀 對你的效果進行非常精細的控制,你甚至可以在動畫過程中觀察觸發的事件,以便處理需要在動畫過程中的設定點執行的其他任務。你可以使用 :hover:focus:target 輕鬆觸發這些動畫,或者透過在父元素上動態新增和刪除類來觸發。

如果你想即時建立動畫或在 JavaScript 中修改它們,James Long 為此編寫了一個簡單的庫,名為 CSS-animations.js

使用 CSS 變換

與其調整絕對定位並自己處理所有數學運算,不如使用 transform CSS 屬性來調整內容的位置、縮放等。或者,你可以使用單獨的變換屬性,如 translatescalerotate。原因還是硬體加速。瀏覽器可以在你的 GPU 上完成這些任務,讓 CPU 處理其他事情。

此外,變換為你提供了你可能不具備的功能。你不僅可以在 2D 空間中平移元素,還可以在三維空間中變換、傾斜和旋轉等等。Paul Irish 從效能角度對 translate() 的優點進行了深入分析(2012 年)。然而,總的來說,你獲得了與使用 CSS 動畫相同的優點:你使用正確的工具完成工作,並將最佳化留給瀏覽器。你還使用了一種易於擴充套件的元素定位方式——如果你使用 topleft 定位模擬平移,則需要大量的額外程式碼。另一個好處是,這就像在 canvas 元素中工作一樣。

注意:根據平臺的不同,如果你希望 CSS 動畫獲得硬體加速,可能需要新增 translateZ(0.1) 變換。如上所述,這可以提高效能。如果過度使用,可能會導致記憶體消耗問題。你在這方面的做法取決於你自己——進行一些測試,找出最適合你特定應用程式的方法。

使用 requestAnimationFrame() 而不是 setInterval()

setInterval() 的呼叫以假定的幀率執行程式碼,而該幀率在當前情況下可能無法實現。它告訴瀏覽器即使瀏覽器實際沒有繪製,也要渲染結果;也就是說,即使影片硬體尚未達到下一個顯示週期。這會浪費處理器時間,甚至可能導致使用者裝置電池壽命縮短。

相反,你應該嘗試使用 Window.requestAnimationFrame()。它會等到瀏覽器真正準備好開始構建動畫的下一幀時才進行操作,並且如果硬體實際上不會繪製任何東西,它也不會打擾。此 API 的另一個好處是,當你的應用程式在螢幕上不可見時(例如,它在後臺並且有其他任務正在執行時),動畫將不會執行。這將節省電池壽命,並防止使用者在夜空中詛咒你的名字。

讓事件立即發生

作為老派的、注重無障礙性的 Web 開發者,我們喜歡點選事件,因為它們也支援鍵盤輸入。但在移動裝置上,這些事件太慢了。你應該改用 touchstarttouchend。原因在於它們沒有延遲,這會讓與應用的互動顯得遲鈍。如果你首先測試觸控支援,你也不會犧牲無障礙性。例如,金融時報就為此目的使用了一個名為 fastclick 的庫,你可以使用它。

保持介面簡潔

我們在 HTML 應用程式中發現的一個主要效能問題是,移動大量 DOM 元素會使一切變得遲緩——尤其是在它們具有大量漸變和陰影時。簡化你的外觀並拖放代理元素會有很大幫助。

例如,如果你有一個很長的元素列表(假設是推文),不要移動所有元素。相反,在你的 DOM 樹中只保留那些可見的元素以及當前可見推文集兩側的幾個元素。隱藏或刪除其餘的。將資料儲存在 JavaScript 物件中而不是訪問 DOM 可以極大地提高你應用程式的效能。將顯示視為資料的呈現,而不是資料本身。這並不意味著你不能使用純 HTML 作為源;只需讀取一次,然後滾動 10 個元素,根據你在結果列表中的位置相應地更改第一個和最後一個元素的內容,而不是移動 100 個不可見的元素。同樣的技巧也適用於遊戲中的精靈:如果它們當前不在螢幕上,則無需輪詢它們。相反,將滾動出螢幕的元素重新用作新進入螢幕的元素。

通用應用效能分析

Firefox、Chrome 和其他瀏覽器都包含內建工具,可以幫助你診斷頁面渲染緩慢的問題。特別是,Firefox 的網路監視器將顯示頁面上每個網路請求發生的精確時間線、請求大小以及所需時間。

The Firefox network monitor showing get requests, multiple files, and different times taken to load each resource on a graph.

如果你的頁面包含執行時間很長的 JavaScript 程式碼,JavaScript 分析器將精確定位最慢的程式碼行。

The Firefox JavaScript profiler showing a completed profile 1.

內建的 Gecko 分析器是一個非常有用的工具,可以提供更詳細的資訊,說明在分析器執行時,瀏覽器程式碼的哪些部分執行緩慢。這使用起來稍微複雜一些,但提供了許多有用的詳細資訊。

A built-in Gecko profiler windows showing a lot of network information.

注意:你可以透過執行 Firefox 並啟用 about:debugging 來將這些工具與 Android 瀏覽器一起使用。

特別是,在移動瀏覽器中進行數十或數百次網路請求需要更長時間。渲染大型影像和 CSS 漸變也可能需要更長時間。即使在高速網路上,下載大檔案也可能需要更長時間,因為移動硬體有時太慢,無法利用所有可用頻寬。有關移動 Web 效能的有用通用技巧,請檢視 Maximiliano Firtman 的 移動 Web 高效能演講。

測試用例和提交 Bug

如果 Firefox 和 Chrome 開發工具無法幫助你找到問題,或者它們似乎表明是 Web 瀏覽器導致了問題,請嘗試提供一個儘可能隔離問題的精簡測試用例。這通常有助於診斷問題。

嘗試透過儲存和載入 HTML 頁面的靜態副本(包括其中嵌入的任何影像/樣式表/指令碼)來重現問題。如果可以,請編輯靜態檔案以刪除任何私人資訊,然後將它們傳送給其他人尋求幫助(例如,提交 Bugzilla 報告,或者將其託管在伺服器上並分享 URL)。你還應該分享使用上述工具收集的任何分析資訊。