無法評估就無法改善。
Lord Kelvin
如要讓 HTML5 遊戲執行速度加快,您必須先找出效能瓶頸,但這項工作可能很困難。評估每秒影格數 (FPS) 資料只是起步,如要全面掌握情況,您必須掌握 Chrome 活動的細微差異。
about:tracing
工具提供的洞察資料,有助您避免採取有意義的猜測,以免為了改善效能而急就章法。這麼做可以節省大量時間和精力,讓您更清楚瞭解 Chrome 在每個畫面中執行的操作,並利用這些資訊改善遊戲效能。
about:tracing
,你好
Chrome 的 about:tracing
工具可讓您深入瞭解 Chrome 在一段時間內的所有活動,而且細節非常多,因此一開始可能會覺得有點難以消化。Chrome 中的許多函式都已針對追蹤功能進行檢測,因此您不必進行任何手動檢測,也能使用 about:tracing
追蹤效能。(請參閱後續章節,瞭解如何手動檢測 JS)
如要查看追蹤檢視畫面,只要在 Chrome 的萬用途徑 (網址列) 中輸入「about:tracing」即可。
您可以透過追蹤工具開始錄製,執行遊戲幾秒鐘,然後查看追蹤資料。以下是資料的範例:
沒錯,這確實令人困惑。接下來,我們來談談如何閱讀這份報表。
每列代表一個正在剖析的程序,左側/右側軸代表時間,每個彩色方塊則是檢測的函式呼叫。資料表包含多種資源類型的資料列。對遊戲分析最有用的資訊是 CrGpuMain,可顯示圖形處理器 (GPU) 的運作情形,以及 CrRendererMain。每個追蹤記錄都包含追蹤期間 (包括 about:tracing
分頁本身) 中每個已開啟分頁的 CrRendererMain 行。
讀取追蹤記錄資料時,第一個工作是判斷哪一列 CrRendererMain 對應至您的遊戲。
在這個範例中,兩個候選值分別是 2216 和 6516。很抱歉,目前沒有任何完善的方法可以挑選應用程式,您只能尋找經常執行定期更新的程式碼行 (如果您已手動使用追蹤點檢測程式碼,則可尋找包含追蹤資料的程式碼行)。在本例中,6516 似乎是根據更新頻率執行主迴圈。如果您在開始追蹤前關閉所有其他分頁,就能更輕鬆地找出正確的 CrRendererMain。不過,仍可能會出現遊戲以外程序的 CrRendererMain 列。
尋找你的裝置
在追蹤工具中找到遊戲的正確資料列後,下一步就是找出主要迴圈。在追蹤資料中,主要迴圈看起來像是重複的模式。您可以使用 W、A、S、D 鍵瀏覽追蹤資料:A 和 D 鍵可向左或右移動 (時間上前後移動),W 和 S 鍵則可放大或縮小資料。如果遊戲以 60Hz 的頻率執行,您可以預期主要迴圈會以每 16 毫秒重複一次的模式運作。
找到遊戲的偵測心跳後,您就可以深入瞭解程式碼在每個影格中執行的確切操作。使用 W、A、S、D 鍵放大畫面,直到您可以閱讀函式方塊中的文字。
這個方塊集合顯示一系列函式呼叫,每個呼叫都以彩色方塊表示。每個函式都由上方方塊呼叫,因此在本例中,您可以看到 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");
上述程式碼會在追蹤檢視畫面名稱中,使用指定的標記建立新的方塊,因此如果您重新執行應用程式,就會看到「更新」和「算繪」方塊,顯示每個標記的開始和結束呼叫之間的時間間隔。
您可以使用這項功能建立人類可讀的追蹤資料,以便追蹤程式碼中的熱點。
GPU 或 CPU?
在硬體加速圖形方面,您在分析時最需要問的問題之一是:這段程式碼是受 GPU 限制還是受 CPU 限制?每個影格都會在 GPU 上執行一些轉譯工作,並在 CPU 上執行一些邏輯;為了瞭解導致遊戲速度變慢的原因,您需要瞭解這兩項資源之間的工作如何平衡。
首先,請在追蹤檢視畫面中找出名為 CrGPUMain 的資料行,該資料行會指出 GPU 在特定時間是否忙碌。
您可以看到,遊戲的每個影格都會在 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");
您現在會看到類似下圖的追蹤記錄:
這份追蹤記錄告訴我們什麼?我們可以看到,影格圖片的時間從約 2270 毫秒變為 2320 毫秒,也就是說,每個影格需要約 50 毫秒 (影格速率為 20Hz)。您可以看到更新方塊旁邊的彩色方塊片段,代表著轉譯函式,但畫面完全由更新函式主導。
與 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);
}
}
使用該著色器的程式碼追蹤記錄會是什麼樣子?
再次提醒,請注意影格時間長度。這裡重複的模式從 2750 毫秒到 2950 毫秒,持續時間為 200 毫秒 (影格速率約為 5 Hz)。CrRendererMain 行幾乎完全空白,表示 CPU 大部分時間處於閒置狀態,而 GPU 則超載。這絕對是著色器過重的信號。
如果您無法確切掌握造成低幀率的原因,可以觀察 5 Hz 更新率,然後嘗試進入遊戲程式碼,並開始嘗試最佳化或移除遊戲邏輯。在這種情況下,這麼做完全沒有幫助,因為遊戲迴圈中的邏輯並不是耗費時間的元兇。事實上,這項追蹤記錄顯示,在每個影格中執行更多 CPU 工作基本上是「免費」的,因為 CPU 會處於閒置狀態,因此將更多工作交給 CPU 不會影響影格所需的時間。
實際範例
接下來,我們來看看實際遊戲的追蹤資料長什麼樣。使用開放式網路技術建構的遊戲有一個很酷的優點,就是您可以查看喜愛的產品有哪些新功能。如要測試剖析工具,您可以從 Chrome 線上應用程式商店挑選喜愛的 WebGL 遊戲,然後使用 about:tracing
進行剖析。以下是從優秀的 WebGL 遊戲 Skid Racer 擷取的追蹤記錄範例。
每個影格大約需要 20 毫秒,也就是影格速率約為 50 FPS。您可以看到 CPU 和 GPU 之間的工作平衡,但 GPU 是需求量最大的資源。如要瞭解如何分析 WebGL 遊戲的實際範例,請試著玩玩看 Chrome 線上應用程式商店中採用 WebGL 技術建構的遊戲,包括:
結論
如果您希望遊戲以 60Hz 的頻率運作,則每個影格中的所有作業都必須在 16 毫秒的 CPU 時間和 16 毫秒的 GPU 時間內完成。您有兩個可同時使用的資源,可以將工作分配給這兩個資源,以便盡可能提高效能。Chrome 的 about:tracing
檢視畫面是一項不可或缺的工具,可讓您深入瞭解程式碼實際執行的內容,並解決正確的問題,進而充分利用開發時間。
後續步驟
除了 GPU,您也可以追蹤 Chrome 執行階段的其他部分。Chrome Canary 是 Chrome 的早期版本,可用於追蹤 I/O、IndexedDB 和其他幾項活動。請參閱這篇 Chromium 文章,進一步瞭解追蹤事件的目前狀態。
如果您是網頁遊戲開發人員,請務必觀看以下影片。這是 Google 遊戲開發人員大使團隊在 2012 年 GDC 大會上,針對 Chrome 遊戲效能最佳化所做的簡報: