zoukankan      html  css  js  c++  java
  • Quantum CSS,一个超快的CSS引擎

    开始

    本文翻译自Inside a super fast CSS engine: Quantum CSS ,如果想要阅读原文,可以点击前往,以下内容夹杂本人一些思考,翻译也并不一定完全。

    碎碎念

    为什么翻译这篇文章尼,一开始只是好奇,基本在前端技术圈子混过都知道火狐正在用Rust语言开发新的浏览器引擎,作为前端开发对火狐的感情还是大大的有(虽然现在已经离不开chrome了),但是还是希望火狐能够再次引领Web的变革。
    可以说前端这几年解决了前端工程化的很多痛点,但是性能这个坎依旧,期望webassembly尽快普及,但是对于前端必定又是一场腥风血雨,前端不会一直是现在这样的前端。既然webassembly出现了,那css怎么办,目前并没听说出现什么新的技术替代它(虽然它真的已经很不适合现代的前端了),那么只能开发一个新的引擎提高性能,这就是火狐家的量子引擎:Quantum CSS(又叫Stylo)。

    加速器

    jet

    这是火狐正在开发的Quantum项目,目的当然是为了让浏览器更快,从上图可以看得到各个模块,而Quantum CSS处于中间位置,这跟它在整个渲染过程中的位置一样,利用Rust可以相当有效利用现代处理器多核心的特性,能够几倍的提速。既然这么厉害,那从哪里可以体验尼:在火狐Nightly版进入about:config设置layout.css.servo.enabled 属性为 true就可以体验这吊炸天的引擎。
    tech

    站在巨人的肩膀上,当然除了利用现代处理器的并行能力,还借鉴当前各家浏览器积累的一些优化技术,接下来会一一解析这些优化技术,如何让引擎更快。

    CSS引擎会干些啥工作

    CSS引擎是浏览器渲染引擎的一部分,而渲染引擎会把我们的HTML和CSS转换成屏幕上的像素(也就是画面吧)。
    各家浏览器都会有自家的渲染引擎,例如谷歌的Blink,Edge的EdgeHTML,Safari的Webkit和火狐的Gecko,虽然有这么多引擎,但是他们都做着同样的事情:把HTML和CSS渲染成我们可以感知的界面。
    而他们内部的工作需要:

    • 首先解析HTML文档,生成DOM节点树,让浏览器可以知道页面的结构和各个节点的关系。
      prase
    • 然后就要弄清楚每个元素长什么样子了,圆的还是方的,有边框还是没边框等等;所以这个时候就需要知道每个元素需要应该用什么样式。
      style
    • 除了知道长的样子,还要知道各个节点的位置和布局。引擎会为所有可见的节点都会创建一个box,但是box并不仅为DOM节点而创建,DOM节点内部也可以有其他box,例如几行文字。
      layout
    • 知道该应用的样式和位置,现在轮到绘制这些box了,这个工作可能会在多个layer上发生。可以认为就像旧时动画制作技术,在一张透明的胶片上绘制背景,在另外的胶片上绘制人物或者其他元素。这样的话就如果人物会动起来的时候,就只用重新绘制人物那张胶片了,不用绘制其他的,这样就省事好多了。
      paint
    • 到了这一步现在我们有不同的胶片(layer),是时候开始合成了(composite),但是合成之前,我们可能还会应用一些transform,旋转啊,偏移啊等等。最后把他们按照层级堆在一起,背景在后,人物在前等等,然后最终渲染到屏幕上。
      composite

    综合后,我们可以知道CSS引擎开始计算样式时需要两样东西:DOM的节点树和一系列的样式规则。
    CSS引擎会遍历所有DOM节点并计算每个节点所应用的样式,它会让DOM节点每个CSS属性都有一个值,就算你在样式表中并没有声明,它可能来自继承或者默认值,或者客户端的样式表(User Agent Style)。
    可以认为引擎就像填表格一样,把这些最后计算出来的值一个一个填进去。
    composite

    为了得到上面的表格,CSS引擎需要做两件事:

    • 搞清楚每个节点所应用的样式规则(selector matching)
    • 把一些你没有声明的值,根据继承或者默认值补充上去(the cascade)

    Selector matching

    首先找出配置当前的节点的样式规则,放到一个list上去,这里也包括客户端的样式表。
    composite

    然后会计算各个样式规则之间的权重,并且根据权重排序。
    composite

    根据权重大小,得出最终应用的样式属性的值。
    composite

    The cascade

    级联(The cascade)目的是为了让CSS更容易编写和维护,由于级联的存在,你可以在body上设置color属性,而li,p,span等元素可以直接使用同样的color,不需要每个元素都要去定义一次。
    为了实现这个功能,CSS引擎会从表格里面寻找一些属性值仍然为空的值,如果属性默认是继承的话,CSS引擎会从父节点那里继承属性值,如果所有父节点都没有定义该属性值的话,就会使用默认的值。
    现在我们的表格都填满了
    composite

    style struct sharing

    上述表格的形式,只是一种表现方式,引擎真实的内部不是这样的。CSS拥有成千上百的属性,如果引擎为每个节点都生成这样一张表,会很快耗掉所有内存。
    相反引擎内部通常会使用style struct sharing,样式的数据会集中在不同对象里面(style struct),然后使用指针指向这些对象。
    composite

    这会很大程度上节省内存,因为各个节点间都很有可能拥有相似的属性值(例如兄弟节点间),另外因为很多属性也是通过继承获取的,所以父节点可以跟子节点间共享这些属性值。

    如何让这些工作更快

    如果我们不去优化这些工作,整个样式计算工作就会是这样:
    composite

    这是巨量的工作,而且并不仅仅在页面加载的时候发生,它会随着用户的交互时刻都在存在(例如hover一个元素,CSS引擎需要从新计算样式)。
    composite

    这样就意味着必须得去优化样式的计算工作,在过去20年,已经测试过不同的优化策略,而Quantum CSS则是组合利用这些最优的优化策略。

    Run it all in parallel

    我们现在的CPU大多拥有多个核心,而Quantum CSS则会把不同DOM节点的样式计算工作分配到不同的核心上去,但是实现也有相当的难度,其中一个原因就是DOM节点树并不一定均匀的,这会导致其中一部分核心工作负荷比其他核心大。
    composite

    为了让各个核心工作负荷更加合理,Quantum CSS使用了一种技术称作 work stealing,当一个DOM节点被处理的时候,引擎可以把它的子节点计算工作分成几个“work units”并且放进队列中。
    当其中一个核心清空自身队列的工作后,它能够寻找其他队列上的其他work units然后执行,这意味着我们不需要提前就去分配好工作,在运行时也会达到最高的工作效率。
    composite

    composite

    在大部分浏览器里面,很难让这种机制毫无错误的运行,而且CSS引擎本身就非常复杂,它在渲染引擎中两个最复杂的模块(DOM和layout)中间。这个过程非常容易产生bug,而且并行程序导致的bug非常难debug,可以通过这篇文章了解更多

    Speed up restyles with the Rule Tree

    对于每个DOM节点,CSS引擎需要遍历所有样式规则去进行selector matching,且对于大部分节点这种matching并不会经常改变。例如,用户hover一个父节点,它的样式规则可能会改变,但是我们仍然需要重新子节点的样式规则去处理属继承的属性值,而子节点之前匹配的规则很有可能不会改变。
    如果我们记录好子节点匹配哪些样式规则,而不用每次都进行一次selector matching,这可能会得到很大的优化。这就是所谓的rule tree,火狐前一个引擎所做的那样。
    CSS引擎会遍历样式规则帮DOM节点找出匹配的选择器,然后根据权重排序,从而创建出一个样式规则的链表,然后将这个链表添加到rule tree中。
    composite

    CSS引擎会尽可能利用已有的分支,为rule tree保持最少的分支数。
    如果大部分链表中大部分的选择器,跟已存在的分支一样,引擎会顺着路径,除非它到达一个节点,rule tree并不存在一样分支,引擎就会添加一个新的分支。
    composite

    在重新计算样式的过程中,引擎会快速检查父节点的改变是否会导致子节点匹配的样式规则改变。如果没有,子节点可以根据自己指向rule tree节点的指针计算样式。引擎会rule tree的节点往上查找,获取整个匹配的规则,从权重最大到权重最小的。这样就可以很轻松的跳过selector matching这一步了。
    composite

    但是这样仍然还有很多工作要做,毕竟一个页面上节点成千上万,这时候并行计算的魔法又可以大显神威了。

    Speed up initial render (and the cascade) with the style sharing cache

    由于整个页面的节点可能会有成千上万个,它们当中很多都匹配着相同的规则。例如wiki页面中每个p元素其实逗匹配着相同的样式规则,拥有一样的computed styles。
    如果这里没有做优化,可能每个段落都要重新计算,但是如果有一种方法来证明每个段落的样式规则都是一样,引擎就只需计算一次就可以了。
    这就是所谓的style sharing cache,由Chrome和Safari所发明的一种优化方式,在引擎处理一个节点后会把computed style放到cache里面,然后开始计算下一个节点的时候,引擎会先检查cache里面是否已经存在计算后的值。
    而这些检查包括:

    • 两个节点是否都有一样的ID和Class等,如果一样它们匹配同样的规则。
    • 对于非selector的行内样式,它们是否拥有一样的值。
    • 它们的父节点都是否指向同一个computed style object,如果一样它们的继承的值都会一样。
      composite

    但是也有很多其他的情况,导致这些检查失效,例如:如果一个CSS规则使用了:first-child选择器,就算两个节点都已经符合上述的规则,结果也会是检查不通过。
    在Webkit和Blink, style sharing cache在这些情况下会放弃检查并不会使用cache。由于大部分网站都使用了这些modern selectors(CSS3),这个优化的作用变得越来越少,所以Blink团队最近把它移除了。
    在Quantum CSS,我们收集了所有这些怪异的选择器(CSS3)然后让它们加入检查。我们会把结果存储为0和1,如果两个元素拥有同样的0和1,那我们就知道他们是匹配同样的样式规则。
    composite

    这样我们就可以继续享受style sharing cache带来的优化。

    结束

    前半部分可以让我们知道CSS引擎的工作内容,后半部分让我们了解新引擎是如何优化性能的,真的学习了很多,我想你也一样。

  • 相关阅读:
    jQuery.messager 使用
    对应后台传json ajax 获取值判断
    easyui datagrid nowrap 使用方法
    easyui combogrid setValues
    去除字符串中所有的逗号
    datagrid loadData 使用方法
    使用git下载一个项目
    java中读取文本文件的时候@Test方法中没有中文乱码,但是@Controller中却有中文乱码
    IDEA启动项目的时候,控制台中显示的都是乱码
    微信流量主点击量与曝光量是什么意思
  • 原文地址:https://www.cnblogs.com/10manongit/p/12209406.html
Copyright © 2011-2022 走看看