背景音乐
macenstein.com 发布了一篇有趣的文章,讨论了当在后台加载包含特定页面集的 Safari 窗口时,其他应用程序出现的性能下降问题。浏览器作为后台应用程序时应该做什么,实际上是一个极其复杂的问题,并非所有决定都像看起来那么明确。
在我深入探讨浏览器处理后台网页的具体细节之前,我想先声明一点:当样本页面集非常小时,仓促得出任何普遍结论都是危险的。在上述引用的示例文章中,作者访问了五个网站并在其中进行了点击操作。这种异常行为可能不仅限于一个网站,甚至可能只与该网站上的某个特定页面有关。为了找出问题所在,最好对每个单独的页面重新进行测试。
人们可能会认为后台浏览器窗口应该什么都不做。然而,一旦你了解当今存在的各种 Web 内容,就会发现这是一个相当天真的假设。在后台运行时,浏览器仍然可以通过多种方式执行必要的工作。以下是一些示例:
(1) 动画 GIF
在后台窗口中清晰可见的动画 GIF 必须继续播放。特别是对于动画 GIF“电影”,人们确实会切换到其他窗口或应用程序,并期望在做其他事情的同时仍能观看电影。在 Safari 2 中,GIF 动画在单独的计时器上运行(每个独立的 URL 对应一个唯一的计时器),因此这会相当消耗 CPU。任何单个 GIF 的最快动画速度是 10 毫秒,因此任何单个 GIF 都会被有效地节流。然而,一旦页面上有许多以不同速度动画的独特图像,你可能会以更快的速率触发计时器。在 WebKit nightly 版本中,我们改进了动画,使其由一个单一计时器驱动所有动画。统一的计时器有助于控制 CPU 使用率,因为如果更新发生得非常接近,它们可以被合并。
在 Safari 2 和 WebKit nightly 版本中,GIF 不会动画,除非它们正在被渲染。如果一个动画 GIF 变得不可见,那么动画将暂停,并且不会消耗 CPU。因此,后台标签页中的所有动画图像都不会动画,直到该标签页中的页面变得可见。即使在前台页面上,如果动画 GIF 滚动到屏幕外,它也会停止动画,直到再次可见。
Safari 处理动画 GIF 的 CPU 使用率表现非常好。(曾有一段时间,MacNN 的读者认为 Safari 存在“动画 GIF 问题”,原因是他们在论坛发帖时输入缓慢,但那个 Bug 实际上与页面顶部和底部的 Flash 广告有关。)
(2) 插件
插件是 Safari 窗口在后台运行时可能发生工作的另一个领域。Mac 插件框架确实非常古老(在 Safari 和 Firefox 中都是如此)。许多插件通过接收“空事件”来进行动画和工作,它们在这些事件中进行处理。你泵送这些事件的速度越快,动画发生的速度就越快,CPU 使用率就越高。Safari 2 实际上会积极地限制这些事件对后台窗口和后台标签页的发送。
然而,当我们这样做时,我们发现用户抱怨后台窗口的“插件缓慢”问题。他们希望能够通过浏览器插件观看视频或收听音频,同时运行另一个应用程序。换句话说,用户希望浏览器即使在后台也能积极地为他们工作。我们实际上在最新的 WebKit 中改变了行为,只对后台标签页进行节流。后台窗口不再被节流,以便插件能够以正常速度正确动画。我们预计,在希望在后台标签页中收听音频的这种情况下,一些用户仍然会对此更改不满意,但我们必须划清界限。
(3) JS 超时和间隔
JavaScript 中的 setTimeout 和 setInterval 是执行重复动画/工作的第三种最常见方式。我们无法真正避免响应后台标签页/窗口中的计时器,因为网页依赖这些工作的实际完成作为 AJAX Web 应用程序的基础。Safari 2 与 Firefox 和 WinIE 有一个关键区别,我怀疑这正是上述文章中描述的额外性能下降的根本原因。
有些网页会指定极短超时时间的重复计时器。事实上,它们经常使用值 0 来表示“尽快触发”。Safari 2 不会限制这些超时,因此一个构造不良的、指定小于 10 毫秒重复计时器的页面实际上会占用大量 CPU。激进的计时器实际上是 Mozilla 长期存在的一个问题,后来才得到修复,它们目前在 Safari 2 中仍然是一个问题。WinIE、Firefox 和 WebKit nightly 版本基本上会对构造不良的页面进行纠错,将小于 < 10 毫秒的计时器值更改为 10 毫秒并忽略它们。我怀疑上述问题的根本原因只是其中一个页面正在通过激进的重复计时器占用 CPU,但我已准备好接受意外。🙂
(4) 跑马灯
跑马灯元素也可以动画。然而,默认情况下,它会受到限制,使其不会消耗过多 CPU(时钟的 60 毫秒滴答)。但是,可以在跑马灯上使用名为 TRUESPEED 的属性,以告知浏览器应尊重实际的动画速率(即使它非常快)。这些页面中存在跑马灯的可能性很小(但并非不可能)。我不认为 Firefox 支持 TRUESPEED 属性,而我们为了与 WinIE 兼容而支持它,这意味着某些跑马灯在 Safari 中可能比在 Firefox 中消耗更多的 CPU。如果是这样,这将是设计使然(并且与 Windows 版 Internet Explorer 相匹配)。然而,这个属性在 Web 上很少出现,因此它不太可能是任何实际性能下降的原因。
(5) XMLHTTPRequests
最后,一个看起来“已加载”的网页可能并非如此,这是因为 AJAX 风格的异步加载请求。由于这些请求不会导致任何进度条或加载动画,这项工作可能非常隐秘(它基本上可以在用户不知情的情况下进行)。然而,通常这些请求获取的数据会快速加载并在另一个线程上进行,因此不太可能显著干扰其他应用程序的运行。
(6) 后退/前进缓存
如果一切按计划进行,进入后退/前进缓存的页面会暂停所有正在运行的动画。这包括插件、跑马灯、JS 计时器和动画图像。有可能某些页面在没有正确暂停的情况下进入了后退/前进缓存。
最后还有几点要说明:
(1)浏览器很少会获得相同的内容,即使在非常流行的网站上也是如此。如果不进行欺骗,很难知道 Safari 是否从问题页面之一获得了有 Bug 的内容。
(2)小样本集不足以得出普遍结论。尝试访问许多其他不同的网站,看看是否仍然出现性能下降。如果仍然出现,那么可能存在系统性问题。但在此之前,我们只知道五分之一个网页正在占用 CPU。
(3)缩小!缩小!缩小!如果可能,缩小问题范围。将其缩小到一个页面。不要来回切换(直接访问页面,而不是通过点击链接跳转)。
请向我们提供精确的重现步骤,这样我们就能验证问题所在并迅速修复。事实上,它可能已经修复了!🙂