效能基礎

效能意味著效率。在 Open Web Apps 的背景下,本文一般解釋了什麼是效能,瀏覽器平臺如何幫助改善效能,以及您可以使用哪些工具和流程來測試和改進效能。

什麼是效能?

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

在所有其他條件相同的情況下,針對除使用者感知效能(以下簡稱 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)向畫布繪製。

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 和 CSS 來對該 HTML 進行樣式設定。

  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.

注意:您可以在 Android 瀏覽器中使用這些工具,方法是執行 Firefox 並啟用about:debugging

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

測試用例和提交錯誤

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

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