--- title: 优化启动性能 slug: Web/Performance/Optimizing_startup_performance tags: - 应用 - 性能 - 游戏 translation_of: Web/Performance/Optimizing_startup_performance ---
一个在软件应用开发中经常被忽视的方面—即便在那些专注于性能优化的软件中—就是启动时的表现。你的应用需要花费多长时间启动?当应用加载时是否会卡住用户的设备或浏览器?这会让用户担心你的应用崩溃了,或者哪儿出错了。花时间确保你的应用能够更好地启动总是一个好主意。这篇文章提供了一些技巧和建议来帮助你达成这个目标,不管是在写一个新的应用还是从其他平台向Web移植一个应用。
不论在什么平台上,尽可能快地启动总是一个好主意。因为这是个很宽泛的问题,在这里我们不会着重关注。相反我们会关注构建 Web 应用时更重要的一个问题:尽可能异步地启动。这意味着不要将你所有的启动代码在应用主线程中的唯一一个事件处理函数中运行。
相反,你应该这样写你的代码,让你的应用在后台线程创建一个 Web worker ,做尽可能多的工作(比如,获取和处理数据)。然后,所有必须在主线程中完成的事情(比如用户事件和渲染用户界面)应该被分成小的片段,这样,当应用启动时,应用的事件循环就可以持续地运行下去。这可以避免应用、浏览器以及/或者设备出现锁死。
为什么异步性很重要?除了上面提出的原因,还考虑了无响应页面或用户界面的影响。如果用户错误地启动了应用,将不能取消。如果应用正在浏览器中运行,用户可能会收到一个“应用无响应”或“加载缓慢”的提醒。你应该显示几种界面,比如进度条,这样用户可以在应用启动时知道他们需要等待多长时间。
如果你从头开始你的项目,通常很容易把所有的东西都写成“正确的方式”,使得代码片段具有合适的异步性。所有纯粹在启动时的计算应该在后台线程中执行,同时使主线程事件的运行时间尽可能缩短。应包含进度指示器,以便用户知道发生了什么以及他们将要等待多久。从理论上来说,无论如何,设计新的应用程序并能很好地启动应该很容易。
但是,另一方面,当您将现有应用程序移植到Web上时,问题会变得棘手。桌面应用程序不需要以异步方式编写,因为通常操作系统会为您处理该问题;或者应用程序当前是唯一正在运行的主要任务,而这具体取决于操作环境。源程序可能有一个主循环,可以被轻松地改成异步操作(通过分别运行每个主循环);启动通常只是一个持续的整体过程,过程中可能会定期更新进度表。
虽然您可以使用 Web workers 异步运行体积巨大,持续时间长的 JavaScript 代码块,但还是要给出一个重大警告:workers 不具备访问 WebGL 或音频的能力,亦不能向主线程发送同步消息,所以你甚至不能将这些 API 代理到主线程中。所有的这一切意味着,除非你够轻松地抽取启动过程中的“纯计算”代码块,加入到 workers 中,否则你最后还是得在主线程上运行大部分或全部的启动代码。
但是即便是那样的代码,通过一点点工作,也可以变为异步的。
关于如何构建你的启动过程,使得其尽可能异步执行,这里有些建议。(不过是新应用还是移植的):
<div id="foo"><!-- <div> ... --></div>
foo.innerHTML = foo.firstChild.nodeValue;
你能通过异步的方式做的事越多,应用就能更好的利用多核处理器。
一旦初始加载完成,应用的主程序开始运行,有可能你的应用一定会是单线程的,尤其当它是个移植的程序时。尝试改善主程序的启动过程时,最重要的事情是将代码重构为小片段。这些小片段可以在你应用的主循环中通过多个调用在分散的代码段中执行,这样主线程就能处理输入和类似的事件。
Emscripten 提供了一个API 帮助处理这种重构;比如你可以用 emscripten_push_main_loop_blocker()
创建一个函数,在主线程可以继续运行前执行。通过创建一个可以依序调用的函数队列,你可以更轻松地在不阻塞主线程地情况下管理代码的运行。
但是这留下了不得不重构现有代码,使程序真正这样运行的问题。这可能需要一些时间。
最好记住大多数浏览器,在你的代码阻塞主线程太长时间——大约10秒左右的时候,就会开始抱怨。理想情况下,你不会在任何地方阻塞那么长时间,但是只要你把时间保持在这之下,就不会有问题。不过要记住,如果有人有个比你更旧更慢的电脑,他们或许会比你经历更长的等待!
除了异步化之外,还有一些其他的事情,可以帮助你改善应用启动时间。这是其中的一部分: