如果無法評估,就無法改善。
凱文
為了讓 HTML5 遊戲執行速度更快,您必須先找出效能瓶頸,但這並不容易。評估每秒影格數 (FPS) 資料只是第一步,但如果您想掌握全局,就必須熟悉 Chrome 活動的細微差異。
關於:追蹤工具可提供深入分析資料,幫助您避免因思索提升效能而缺少問題之替代方案,但這基本上就是出於意料之外的猜測。這麼做可省下大量時間和精力,讓您更清楚瞭解 Chrome 在每個頁框的執行動作,並利用這些資訊改進遊戲。
歡迎加入:tracing
Chrome 的 about:Chrome 的許多功能都可以讓您直接追蹤,因此您不必進行任何手動操作,仍可使用 about:trac 追蹤應用程式效能。(詳情請參閱後續章節,瞭解如何手動檢測 JS)。
如要查看追蹤記錄檢視畫面,請輸入「about:tracing」。貼到 Chrome 的網址列
![Chrome 網址列](https://web.developers.google.cn/static/articles/abouttracing/image/chrome-omnibox-cd7c2b0952da3.png?authuser=19&hl=zh-tw)
透過追蹤工具,您可以開始錄製、執行遊戲幾秒鐘,然後查看追蹤資料。資料的範例如下:
![簡易追蹤記錄結果](https://web.developers.google.cn/static/articles/abouttracing/image/simple-tracing-result-1d9ca669e5fb8.png?authuser=19&hl=zh-tw)
沒錯,這真的令人困惑。我們來談談讀取方式
每列代表一個正在進行剖析的程序,右軸表示時間,每個彩色方塊都是檢測設備呼叫。某些資源會有不同的資料列。對遊戲剖析作業最感興趣的項目為 CrGpuMain,其中顯示圖形處理器 (GPU) 和 CrRendererMain,在追蹤期間,每個追蹤記錄都包含每個開啟分頁的 CrRendererMain 行 (包括 about:tracing 分頁本身)。
讀取追蹤記錄資料時,第一步是決定與遊戲對應的 CrRendererMain 資料列。
![醒目顯示簡易追蹤記錄結果](https://web.developers.google.cn/static/articles/abouttracing/image/simple-tracing-result-hig-27a40f191d328.png?authuser=19&hl=zh-tw)
在這個範例中,兩位候選人為:2216 和 6516。可惜的是,目前沒有完善的選擇應用程式方式,除了尋找定期更新的行 (或者您已使用追蹤點手動檢測程式碼,尋找含有追蹤記錄資料的行)。在這個例子中,6516 的執行頻率是執行與更新頻率有關的主要迴圈。如果您在開始追蹤記錄前關閉所有其他分頁,要找出正確的 CrRendererMain 就會比較容易。但除了遊戲,其他程序可能仍有 CrRendererMain 資料列。
正在尋找影格
在追蹤工具中找出正確的遊戲資料列後,下一步就是找出主要迴圈。主迴圈在追蹤資料中看起來像重複模式。您可以使用 W、A、S、D 鍵 (A 和 D 鍵) 來左右移動 (來回移動),W 和 S 鍵則可放大和縮小資料。如果遊戲以 60Hz 為單位執行,主要迴圈應為每 16 毫秒重複一次的模式。
![執行框架似乎有三個](https://web.developers.google.cn/static/articles/abouttracing/image/looks-three-execution-fr-f47b55db5f581.png?authuser=19&hl=zh-tw)
找出遊戲的心跳後,您就可以深入瞭解程式碼在每個影格中執行的動作。使用 W、A、S、D 來放大檢視,直到您可以閱讀功能方塊中的文字。
![深入執行執行影格](https://web.developers.google.cn/static/articles/abouttracing/image/deep-an-execution-frame-c862ec9cb9343.png?authuser=19&hl=zh-tw)
這組方塊會顯示一系列函式呼叫,每個呼叫都以一個顏色方塊表示。上面的方塊會呼叫每個函式,因此在這個例子中,您會看到 MessageLoop::RunTask 稱為 RenderWidget::OnSwapBuffersComplete,而它叫做 RenderWidget::DoDeferredUpdate,依此類推。讀取這項資料,可讓您全盤掌握每次執行作業所花費的時間和所需時間。
但這裡有一點黏著度。about:tracing 所公開的資訊為 Chrome 原始碼的原始函式呼叫。您可以從名稱中推測每個函式的用途,但資訊未必會讓使用者一目瞭然。瞭解影格的整體流程確實很有幫助,但您需要更清晰易讀的資訊,才能確實瞭解影格本身。
新增追蹤記錄標記
幸好,您可以在程式碼中新增手動檢測方法,建立追蹤記錄資料:console.time
和 console.timeEnd
。
console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");
上述程式碼會在追蹤檢視畫面名稱中以指定標記建立新方塊,因此如果您重新執行應用程式,就會看到「更新」和「顯示」方塊,顯示每個廣告代碼開始和結束呼叫所經過的時間。
![手動新增的標記](https://web.developers.google.cn/static/articles/abouttracing/image/tags-added-manually-97406d4b80c0e.png?authuser=19&hl=zh-tw)
您可以運用這個方法,建立人類可讀的追蹤資料,在程式碼中追蹤熱點。
還是 CPU?
使用硬體加速圖形時,您在剖析時最常提出的問題之一是:這個程式碼是否受到 GPU 限制?在每個影格中,您需要在 GPU 及 CPU 上執行某些邏輯轉譯作業;為了瞭解導致遊戲速度緩慢的原因,您需要查看兩種資源之間的工作平衡。
首先,在追蹤記錄檢視畫面中找出名為 CrGPUMain 的線條,這代表 GPU 在特定時間是否忙碌。
![GPU 和 CPU 追蹤記錄](https://web.developers.google.cn/static/articles/abouttracing/image/gpu-cpu-traces-4f4eb3986da83.png?authuser=19&hl=zh-tw)
您可以看到遊戲的每個影格都會在 CrRendererMain 和 GPU 上執行 CPU。上述追蹤記錄相當簡單的用途,其中 CPU 和 GPU 都處於閒置狀態,因此每個 16 毫秒影格中的大部分時間都處於閒置狀態。
當您的遊戲運作速度緩慢,而且您無法確定哪個資源最大化時,追蹤檢視畫面就相當實用。瞭解 GPU 和 CPU 行間的關聯是偵錯的關鍵。採用與先前相同的範例,但在更新迴圈中增加一些額外工作。
console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");
console.time("render");
render();
console.timeEnd("render");
現在,您會看到如下所示的追蹤記錄:
![GPU 和 CPU 追蹤記錄](https://web.developers.google.cn/static/articles/abouttracing/image/gpu-cpu-traces-803b8ee665596.png?authuser=19&hl=zh-tw)
這個追蹤記錄提供了什麼資訊?我們可以看見顯示影格大約需要 2270 毫秒至 2320 毫秒,也就是每個影格大約需要 50 毫秒 (20 Hz 的畫面更新率)。您可在更新方塊旁看到代表轉譯功能的彩色方塊和色塊,但影格本身都是以更新方式呈現。
與 CPU 中的內容相比,您可以看到 GPU 在大多數影格上都處於閒置狀態。如要最佳化這段程式碼,請尋找可在著色器程式碼中完成的作業,並將這些作業移至 GPU,充分運用資源。
如果著色器程式碼本身速度緩慢,但 GPU 過載,該怎麼辦?如果從 CPU 移除不必要的工作,改為在片段著色器程式碼中新增工作,會發生什麼事。以下是不需要昂貴的片段著色器:
#ifdef GL_ES
precision highp float;
#endif
void main(void) {
for(int i=0; i<9999; i++) {
gl_FragColor = vec4(1.0, 0, 0, 1.0);
}
}
使用該著色器的程式碼追蹤記錄會是什麼樣子?
![使用慢速 GPU 程式碼時的 GPU 和 CPU 追蹤記錄](https://web.developers.google.cn/static/articles/abouttracing/image/gpu-cpu-traces-using-sl-ffb0094996429.png?authuser=19&hl=zh-tw)
再次注意影格的時間長度。這裡的重複模式從 2750 毫秒到 2950 毫秒,持續時間是 200 毫秒 (約 5Hz)。CrRendererMain 行幾乎完全空白,意味著 CPU 在大部分時間處於閒置狀態,而 GPU 超載。這代表著色器過重。
如果您無法確實掌握造成影格速率低落的具體原因,可以觀察 5 Hz 更新作業的發生原因,而想嘗試對遊戲程式碼進行最佳化或移除遊戲邏輯。在這種情況下,這種做法絕對沒有問題,因為遊戲迴圈中的邏輯並不是耗費時間。事實上,這項追蹤記錄顯示執行更多 CPU 工作,基本上是「免費」是因為 CPU 會在閒置狀態下等待,因此,多餘的工作就不會影響影格所需的時間。
實際範例
現在,我們來看看實際遊戲中的追蹤資料是什麼樣子。針對採用開放網路技術所建構的遊戲,其中一項酷炫之處,就是您可以看到喜愛產品中的動靜。如要測試剖析工具,請在 Chrome 線上應用程式商店中挑選喜愛的 WebGL 標題,並使用「 about:tracing」。這就是從優良 WebGL 遊戲競速選手取得的追蹤記錄範例。
![追蹤真實遊戲](https://web.developers.google.cn/static/articles/abouttracing/image/tracing-real-game-adc53901fdb79.png?authuser=19&hl=zh-tw)
看來每個影格大約需要 20 毫秒,也就是影格速率約為 50 FPS。您可以看到工作在 CPU 和 GPU 之間達到平衡,但 GPU 則是需求最多的資源。若想看看分析 WebGL 遊戲的實際實例,可以試試使用 WebGL 建構的部分 Chrome 線上應用程式商店遊戲,包括:
結論
如果希望遊戲以 60Hz 的速度執行,所有作業都必須搭配 16 毫秒的 CPU 和 16 毫秒的 GPU 時間。兩項資源可並行運用,並可在兩者之間切換作業以提高成效。Chrome 的關於:
後續步驟
除了 GPU,還可以追蹤 Chrome 執行階段的其他部分。Chrome Canary 是 Chrome 的早期階段版本,可用來追蹤 IO、IndexedDB 和其他多項活動。如要進一步瞭解追蹤事件目前的狀態,請參閱這篇 Chromium 文章。
如果您是網頁遊戲開發人員,請務必觀看以下影片。這是 Google 遊戲開發人員服務代表團隊 2012 年遊戲開發人員服務代表團隊的簡報,內容旨在分享 Chrome 遊戲的效能最佳化相關資訊: