使用 about:tracing 标志对 WebGL 游戏进行性能分析

莉莉·汤普森
Lilli Thompson

如果无法衡量,就无法改进。

开尔文勋章

为了让您的 HTML5 游戏运行得更快,您必须先找出性能瓶颈,但这可能会很困难。评估每秒帧数 (FPS) 数据只是一个开始,但若要全面了解情况,您必须了解 Chrome 活动的细微差别。

about:tracing 工具提供的分析数据有助于避免那些旨在提升性能的仓促解决方法,但这些都是出于好主意的猜测。这不仅能节省大量的时间和精力,还能更清楚地了解 Chrome 处理每一帧的情况,并利用这些信息优化您的游戏。

您好 about:tracing

Chrome 的 about:跟踪工具为您提供了一个窗口,可让您查看一段时间内 Chrome 的所有活动,细化程度非常高,以至于您一开始可能会觉得很繁琐。Chrome 中的许多功能都可开箱即用,因此无需任何手动插桩,您仍然可以使用 about:tracing 来跟踪性能。(请参阅稍后的有关手动插桩 JS 的部分)

要查看跟踪视图,只需在 Chrome 的多功能框(地址栏)中输入“about:tracing”即可。

Chrome 多功能框
在 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 键放大地图,直到您能够读清功能框中的文字为止。

深入执行帧
深入了解执行帧

此集合的框显示了一系列函数调用,每个调用用一个彩色框表示。每个函数均由其上方的框调用,因此在本例中,您可以看到名为 RenderWidget::OnSwapBuffersComplete 的 MessageLoop::RunTask,后者又调用了 RenderWidget::DoDeferredUpdate,依此类推。通过读取这些数据,您可以全面了解执行什么以及每次执行所用的时间。

但这里会有点粘稠的。about:tracing 公开的信息是 Chrome 源代码中的原始函数调用。您可以根据名称对每个函数的用途做出有根据的猜测,但这些信息并不完全易于使用。查看帧的整体流动非常有用,但是您需要更便于用户阅读的内容才能真正弄清楚发生了什么。

添加跟踪记录标记

幸运的是,您可以通过一种简单的方法在代码中手动插桩,以创建跟踪数据:console.timeconsole.timeEnd

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

上述代码会在跟踪视图名称中创建新框,其中包含指定的标记,因此,如果您重新运行应用,会看到“update”和“Rendering”框,其中显示每个标记的 start 和 end 调用之间相隔的时间。

手动添加的标签
手动添加的标记

通过这种方式,您可以创建直观易懂的跟踪数据,以跟踪代码中的热点。

GPU 还是 CPU?

对于硬件加速图形,在分析过程中您可以提出的最重要的问题之一是:此代码是受 GPU 限制还是受 CPU 限制?对于每个帧,您需要在 GPU 上执行一些渲染工作,在 CPU 上执行一些逻辑;要了解导致游戏运行缓慢的原因,您需要了解这两项资源之间的工作平衡。

首先,在跟踪视图上找到名为 CrGPUMain 的行,它指示 GPU 在特定时间是否处于忙碌状态。

GPU 和 CPU 跟踪记录

您可以看到,游戏的每一帧都会引起 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 跟踪记录

此跟踪记录说明了什么?我们可以看到,图片中的帧大约从 2270 毫秒缩短到 2320 毫秒,这意味着每帧大约需要 50 毫秒(帧速率为 20Hz)。您可以看到,更新框旁边有几条表示渲染函数的彩色框,但该帧完全由更新本身主导。

与 CPU 的情况相反,您可以看到 GPU 在每一帧的大多数情况下都处于空闲状态。为了优化此代码,您可以查找可以在着色器代码中完成的操作,并将其移至 GPU,以充分利用资源。

当着色器代码本身运行缓慢且 GPU 过度劳累时,该怎么办?如果我们从 CPU 中移除不必要的工作,改为在 fragment 着色器代码中添加一些工作,该怎么办?这里有一个不必要的 Fragment 着色器:

#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 跟踪记录
使用慢速 GPU 代码时的 GPU 和 CPU 跟踪记录

同样,注意每一帧的时长。在该示例中,重复模式从约 2750 毫秒增加到 2950 毫秒,持续时间为 200 毫秒(帧速率约为 5Hz)。CrRendererMain 行几乎完全为空,这意味着 CPU 在大部分时间都处于空闲状态,而 GPU 过载。这充分表明您的着色器过重。

如果您无法确切了解导致帧速率偏低的原因,则可以观察到 5 Hz 的更新情况,并试图访问游戏代码并开始尝试优化或移除游戏逻辑。在这种情况下,这样做肯定是不利的,因为游戏循环中的逻辑并不是在占用时间。事实上,此轨迹表明每个帧执行更多的 CPU 工作基本上属于“空闲”,因为 CPU 会在空闲时挂起,所以提供更多工作不会影响帧的用时。

真实示例

现在,我们来看看来自真实游戏的跟踪数据是什么样的。使用开放网络技术构建的游戏最棒的一点是,您可以查看自己喜欢的产品中的动态。如果您想测试分析工具,则可以从 Chrome 应用商店中选择您喜爱的 WebGL 游戏,然后使用 about:tracing 对其进行性能分析。这是从出色的 WebGL 游戏 Skid Racer 中获取的轨迹示例。

描绘真实游戏
跟踪真实游戏

每帧似乎需要大约 20 毫秒,这意味着帧速率约为 50 FPS。可以看到,工作在 CPU 和 GPU 之间是平衡的,但 GPU 是需求最大的资源。如果您想看分析 WebGL 游戏的真实示例,请尝试玩一些使用 WebGL 构建的 Chrome 应用商店游戏,包括:

总结

如果您希望游戏以 60Hz 的频率运行,那么对于每一帧,所有操作都必须占用 16 毫秒的 CPU 和 16 毫秒的 GPU 时间。您有两个可并行使用的资源,并且可在它们之间切换工作以最大限度地提升效果。Chrome 的 about:跟踪视图是一款非常实用的工具,可以帮助您深入了解代码的实际运行情况,并帮助您解决相应的问题,从而最大限度地缩短开发时间。

后续操作

除了 GPU 之外,您还可以跟踪 Chrome 运行时的其他部分。Chrome Canary 版是 Chrome 的早期版本,可进行插桩 (instrument) 处理以跟踪 IO、IndexedDB 和多项其他活动。您应阅读这篇 Chromium 文章,以便更深入地了解跟踪事件的当前状态。

如果您是网络游戏开发者,请务必观看下面的视频。这是 Google 游戏开发技术推广工程师团队在 2012 年游戏开发者大会上发布的一个关于 Chrome 游戏性能优化的演示文稿: