尽管设备性能不断提高,但Web对内存和处理器的要求越来越高。 跨选项卡正确渲染和智能分配资源是解决此问题的重要组成部分。 Konstantin Kramlih
PurplePowder在“ I Frontend”
会议上的演讲致力于在Chromium项目和Yandex.Browser中提高生产率并节省资源的算法。
其中一些(例如Hibernate技术)已经在
单独的帖子中分类了。 骨骼报告涵盖了更广泛的问题:不仅从切换选项卡的角度来看,而且还考虑了呈现内容,图块和页面层的方法。
最后,Web界面开发人员可以学习如何识别和解决网站性能问题。
-我叫Kostya,是Yandex.Browser团队内部组件开发小组的负责人。 在浏览器中,五年来我一直在做不同的事情:从浏览器中的所有解码,所有HTML5视频到渲染,渲染和其他类似过程。
在过去的一年半到两年中,我参与了与节省浏览器资源有关的项目:CPU,内存,电池。
您说-Kostya,2019年在院子里,资源问题出在哪里? 您可以使用任何资源购买所需的任何设备。 但是,如果我们转到开放的Mozilla统计信息,我们将看到一半的用户具有4 GB或更少的内存。 而拥有一两个物理核心的许多用户,则在您的受众中占了相当大的比例。 在这个世界上我们生活。

多少人经常看到此标签? 对于只有很少的RAM和旧计算机的普通用户来说,这正是发生的情况。

怎么办 这个问题对任何人都不是秘密,他们大约在三年前开始积极与之抗争。 从那时起,螺母已通过各种方式逐渐拧紧。 让我向您展示一个有关2016年左右Stack Overflow的示例。 这是一个非常简单的代码段。 这件事只是更新标题,并设置自上次启动此功能以来已过去的时间。 理想情况下应获得什么? 标题中的每100毫秒应写入+ -100。 真幸运

但是,如果我们打开并这样做呢? 有人碰到这个吗? 问题出在Stack Overflow:Cookie Clicker到底在我的后台标签中停止运行了什么? 这是Chromium旨在减少浏览器CPU使用率的首批举措之一。 这个想法是,如果用户现在不使用该标签,那么他现在就不需要它了-让我们在上面放上JS。
浏览器尝试将此选项卡上的CPU负载保持在大约1%-它开始暂停各种计时器,JS执行等。这是光明的未来的第一步。

在浏览器中放置一段时间后,您会发现背景标签完全停止工作。 这是我正在谈论的光明的未来。 根据Chromium的计划,他们在上一届BlinkOn上表示,计划在2020年这样做:让标签页加载,如果它是背景,则它什么也不会做。 您始终需要为此做好准备。

在Yandex.Browser中,我们也处理了这样的问题,但是没有明确地解决它,并且没有破坏整个网络。 我们创建了一种节能模式,该模式可以关闭处理器上的解码功能,而只保留视频卡上的解码功能,还可以降低FPS并禁用一些目前不需要的动画,而用户应该节省电池电量。 这给了我们大约一小时的额外电池寿命。 任何人都可以检查,例如ixbt。

我想有人会说:Kostya,您“打破了”网络,对某些用户有所帮助,但没有提出任何更明智的选择。 添加铁杆! 浏览器如何绘制页面?

简而言之,层的概念是浏览器试图将页面分成层并分别绘制时。 这样做是为了执行某些动画,并且不强制重画静态的动画。 浏览器以不同的启发式方式执行此操作。 例如-尝试将视频元素选择到一个单独的图层中,显然,该图层可以快速绘制并经常重新绘制。 而且,如果它显示在某处,则无需重新绘制其下的所有内容。
另外,每一层都被分为这样的图块-256 x 256矩形在检查器中您可以看到类似的内容。 有一个框架,分为一堆瓷砖。


它是什么,为什么需要它? 首先,要优先考虑渲染。 如果我们有一个巨大的香肠,我们都可以在上面绘制,那么如果用户现在只能看到自己在ViewPort中所拥有的,那为什么还要绘制所有这些?

使用这种方法,我们首先只绘制用户当前在ViewPort中看到的内容,然后绘制一个磁贴,然后再沿滚动方向绘制。 如果用户向下滚动-向下滚动,如果向上滚动-向上滚动。 仅当我们有这些磁贴的配额并且可以绘制它们时,其他所有内容都将被绘制,之后用户将可以看到它们。 也许永远不会。

它还对残疾有很大帮助。 假设用户打开一个页面,选择了一块,而我们不需要重绘所有内容。 我们可以保留大多数以前的渲染。 我们将在此处重新绘制六个图块,一切都会好起来。

在此级别上,进行了一些非常成功的优化。 例如,Chromium在2017年左右进行了此类优化。

如果我们只有一个很小的渲染,我们只会这样做。 然后光标闪烁,我们仅重绘光标区域,而不重绘此图块的整个区域。 我们非常节省CPU,以免重新绘制所有内容。

它还有助于节省内存。 这是什么问题? 整个白色矩形。 想象一下它将是256 x 256纹理,每个像素四个字节。 尽管看起来该区域只能用五个数字编码:坐标,宽度,高度和颜色。

在Chromium中,单色区域的优化已经完成。 如果浏览器了解到此矩形中没有渲染,它完全是单色的,不是透明的,并且满足其他一些条件,那么我们只是告诉视频卡-绘制一个白色矩形,不要选择整个纹理。
还有什么可以优化的呢? 如果您查看其余的图块-它们的内容很少,并且白色区域很大。 在Yandex.Browser,我们考虑了这一点,并提出了一种称为自适应平铺的机制。

有一个小矩形,一块瓷砖。 磁贴中间几乎没有内容。 我们选择它,并且-仅在它下面-纹理。 其他所有内容也分为几个区域,这就是我们在讨论视频卡的地方:只需在此处绘制白色即可。

该页面还将开始保存以红色突出显示的所有内容。 在更复杂的页面上,看起来像这样。

重要的是要理解,仍然有一堆图层,并且每一层都是这样绘制的。 在每一层上,您可以节省一些内存。 这种方法使我们为所有用户平均节省了大约40%的视频内存。
更铁杆! 他们在这里节省了一些内存,他们“破坏”了网络-为什么不进一步“破坏”网络呢?
在Chromium中,有一项类似的政策:如果用户不使用背景标签,则如果他离开了背景标签,则不需要它们。 如果现在我们没有内存,并且浏览器已经放下,那么让我们使用用户很长时间没有使用的最旧的选项卡,然后取消它。 她将保留在界面中,但是该过程将不再存在,所有JS都将消失。 可以吗? 在前端聚会上问这样的问题,并期望除了“您在做什么?”以外的其他答案,这很奇怪。

然后,它没有走太多。 以下是Chromium博客的真实评论:您为我破坏了我的所有应用程序,有一种游戏-跳,它没有状态。 重要的是要了解那里的卸载处理程序不起作用,就像我们只是简单地放置了此选项卡一样。 用户然后返回到它,然后我们从网络重新加载它,好像什么都没发生。
然后,这种方法被暂时放弃,并提出了一个更周到的认真想法。 他们称她为废品。

有什么意义? 这与制表符的杀死相同,只是受控的。 它称为智能单词
Page Lifecycle API 。 如果您有一个标签,并且用户很长时间没有看到它,则它可能会进入冻结状态。 浏览器在整个活动中说:我现在要冻结你。 处理完事件后,将不会执行任何操作。 做您需要的准备。
然后它可以通过resume事件退出冻结状态,据说什么也没发生。 或者,如果浏览器现在真的需要释放内存,它只会占用并杀死它。 但是,如果用户返回到该标签,我们将重新加载该标签并为文档设置“已丢弃”字段。

现在,您已经可以订阅这些事件并以某种方式捕获它们。 如果选项卡确实被杀死,则可以检查被丢弃的字段。 这意味着丢弃后已恢复。 您可以还原以前的状态。

Yandex.Browser我们几年前曾想过:为什么不使用复杂的基本方法。 他们称他为冬眠。

有什么意义? 有几个选项卡,某种JS正在运行,某种状态。 每个选项卡都会创建一个单独的过程:此处可以播放视频,此处您可以在表单中保留一些内容。 休眠到了-没有任何进程。 我们都得到了他们。 但是,如果现在切换回这些选项卡,则过程将返回,整个状态将保持在适当的位置,视频将在正确的时刻继续播放,字段中的所有文本都将保留在适当的位置。

我们做了什么? 每个渲染器中包含三个最重要的内容:运行整个JS的V8,存储整个DOM的Blink,以及某种浏览器绑定,这有助于将所有内容以及选项卡和其他内容连接在一起。

例如,考虑一个样本。 在这里,我们等待onload发生,并将新的div元素添加到DOM树。 对于浏览器,它看起来像这样。

自然地,有一棵DOM树,它有一些字段,关联的对象,并且有这样一个实体。

在V8中,存储了每个节点的状态,并且这些节点通过绑定程序层与眨眼对象相关联。 我们做了什么? 我们从V8中获取了序列化程序,对整个V8状态进行了序列化,在Blink中找到了所有相关对象,编写并添加了序列化程序来保存整个DOM树,对其进行序列化,然后写入磁盘,进行压缩和加密。 我们还教浏览器从快照中恢复。 也就是说,当用户转到此类选项卡时,我们将其展开,解密并显示给用户,然后将其完全还原。 (
有关Hibernate的另一篇文章 -大约。)
现在,Hibernate可以为每个人稳定地发布,并允许每个用户平均保存一个或两个选项卡。 也就是说,他平均总是保存一个选项卡,或者也许两个。 与我们一样,这样做可以为具有10个以上标签的用户节省内存-但我们不具有代表性。
我告诉了浏览器如何提供帮助,但是现在每个人都可以做一些事情来加快网站速度,提高网站性能。 您可以今天就去做。
首先,您需要了解是否存在内存问题。

有某些症状:站点开始退化或出现褪色。 通常,这意味着将触发垃圾收集器,整个世界都被冻结,什么也没画。 或网站只是不断减速-这种情况也会发生。
您需要了解是否有问题。 我们看看JS内存会发生什么。

如果它来回跳跃或持续增长,这不是一个好症状。 或持续增长。

在这种情况下,您始终可以通过DevTools拍摄快照。 有没有人甚至对JS的泄漏感到困扰? 如果有人不知道,那么在JS中很有可能会泄漏。

例如,您有一个全局变量,在其中添加未插入树中的节点。 您忘记了它们,然后发现它们正在吞噬数百KB甚至兆字节。

严格来说,不清楚如何处理此类分离的节点。 您可以将它们从树中拔出,然后再插入。 因为通常图片的状态或其他内容保留在其中。 因此,您可以找到它们并解决问题。

您还可以在时间轴上查看内存分配。 如果您有系统的出院并且从不清理,这也是一个不好的信号,我想每个人都可以理解。

您可以在选项卡上播放图层。 在这方面,浏览器通常使用启发式方法-它尝试将自己认为经常更改的对象分离为单独的层。 但是有时它的性能不是很好,事实证明您有很多层会占用大量内存。 您总是可以查看是否有这种情况,消除一些层并找出该层占用了多少内存。

问题的另一层是性能。 setTimeout对于动画来说是个坏主意。 它与浏览器呈现页面的方式不兼容。 它可能无法分阶段工作:浏览器请求了一个帧,但是还没有动画,因为setTimeout不起作用。 或者即使您不需要绘制任何东西,它也可以工作。 用户已经离开,仍然有一些后台工作,但是我们将通过setTimeout与他一起工作。
这是仅当浏览器需要绘制此页面时回调才调用的正确方法。

我还想提醒您,如果浏览器要每秒绘制60帧,则意味着每帧需要大约16毫秒。

而且,如果您占用主流时间超过16毫秒,那么您就可以保证跳帧,这对于用户来说是非常不愉快的:该站点开始变得混乱。 因此,所有的辛苦工作都正确地放在了后台线程中,您只需返回结果即可。

或者另一种方法是使用微任务。 创建一个任务队列,一段时间后处理它,直到配额结束,然后让浏览器平静地绘制页面。

当然,您可以添加新层。 浏览器使用启发式方法,并尝试选择认为必要的层。 但是,如果您现在知道此元素将被设置为动画,则最好明确告知浏览器您需要在单独的图层中选择此元素。 然后将更有效地绘制它。

最新的有趣方法-抱怨。 如果您有问题,经常来聊聊会很有帮助。 也许这个问题很容易解决,我们会互相帮助。
一点背景。 该功能目前正在制定中。 我们有一个搜索引擎开发页面团队。 它们具有渲染第一个代码段的时间之类的指标。 如果用户以前看到您需要单击的第一个链接,并且很可能会给他正确的答案,那么最好尽快绘制它。 他们来了,问这一次是否有可能加快速度,因为他们都被挤出了。
我们进行了调查,进行了性能测试,制作了原型,结果证明,如果现场告诉我们首先要画什么,我们就可以这样做。 我们进行了测试,发现它可以改善我们的性能。 现在,我们正在针对少量受众进行生产测试。 我们看看会发生什么。 请继续关注。
世界不会停滞不前。 用户开始越来越需要,浏览器在变化,网络在变化。 这很正常。 我们不仅尝试制作食品特色,还尝试以各种可能的方式帮助用户在内容消费方面获得最佳体验。 而且,您始终需要为任何流行的浏览器中的更改做好准备。 自然,如果有问题-来吧,我们准备互相讨论并互相帮助。
在这里,我整理了各种有用的链接: