[关闭]
@rogeryi 2015-05-29T12:28:46.000000Z 字数 2842 阅读 4013

Chromium 代码研究的一些感想

Chromium


自己研究 Chromium 的代码(主要是 Android WebView 这个平台的代码),也有相当长的一段时间了,在这里把自己的一些感想记录下来,如果对他人有所帮助,也算是有些益处。

在研究过程中最大的感受,Chromium 非常重视架构的合理性和灵活性,每一个模块内聚性,独立性,还有接口和实现的分离都做的很好,对外的接口十分清晰,包括供外部调用的接口,需要外部提供实现的抽象接口(XXXClient,XXXDelegate),让外部可以获得事件通知的接口(XXXObserver)等等。总的来说,在几个重要的非功能性目标里面,Chromium 给我的感觉它们排列的优先级是这样的:

  1. 架构的合理性和灵活性
  2. 安全性
  3. 性能
  4. 资源占用(CPU/GPU/内存)

内聚,独立的模块,能够在模拟的测试环境中进行模块测试/单元测试,可以在运行时灵活地选择不同的配置,实现架构的灵活性,让 Chromium 更容易适应不同的平台(某种程度而言,对架构灵活性的追求也来源于 Chromium 需要适配多个平台,并且不同的平台差异很大,从一个 WebView 组件到一个浏览器,甚至是一个完整的操作系统)。

乍一看,这些应该都是优点吧,但任何事物都有其矛盾对立的另外一面,对研究 Chromium 的人来说,架构的灵活性也意味着系统复杂度的增加,系统的层次更多了,要分析实际的运行流程更困难了,很多抽象层仅仅是为了隔离不同模块的直接依赖关系,它们代码的实际含义并不明显,这些都增加了研究的困难,特别是当你只希望专注某一个特定的平台上,比如 Android WebView,很多中间层次,很多抽象接口,很多各种各样不同的实现对你来说不仅是毫无意义,实际上还造成了干扰。这也是我在之前的一些分享会议上面说过的,对于我们研究 Chromium Android WebView 代码的同学来说,当前的状况其实是满不公平的,Android WebView 本来是一个架构相对比较简单的平台,但我们又不得不承担其它平台带来的研究复杂度的增加...

单纯是研究复杂度增加也就罢了,但实际上灵活的架构,复杂的系统很容易大大增加资源的占用,而资源的占用又容易导致性能的下降。怎么正确评估 Chromium 的性能是一个很有趣的问题,一方面来说,Chromium 的性能并不差,特别是 Blink 内核在 JS/DOM/CSS 等关键指标上表现很优秀,整体而言 Chromium 各方面的性能指标都比较均衡,没有明显的短板,对于各种类型的网页都有不错的表现。但是对一个浏览器来说,Chromium 非常复杂,资源占用比较厉害(CPU/GPU/内存),在移动设备这样的资源受限环境上,又很容易因为资源不足而导致实际的性能表现不佳,或者很难达到完美的水平。另外 Chromium 性能优化的思路通常是通过更复杂的架构,将工作分解到更多不同线程,通过更高的并发程度来提高性能,但这种优化方式又进一步地增加了资源的占用,实际的效果还很难说。

Chromium 的内存占用是一个比较严重的问题,比方说对比 Android 4.3 的 WebView(非 Chromium 内核),Android 5.0 的 WebView 每一个硬件合成器都有自己的光栅化缓存池和纹理缓存池,这意味着如果 View Hierarchy 上同时存在多个 WebView,即使只有一个可见,不可见的 WebView 仍然会占用着大量的光栅化缓存和纹理缓存,而在 Android 4.3 时,多个 WebView 的硬件合成器是共享同一个缓存池的,缓存的利用率更高,缓存占用也不会随着 WebView 个数的增加而线性上涨(同一个缓存池意味着不活跃的 WebView,缓存可以转移给活跃的 WebView 使用)。Chromium 这样的设计很大程度是因为让合成器更独立和自完备,在渲染架构上更灵活,可以适应不同的配置,这也意味着官方很难去做类似 Android 4.3 WebView 这样的优化,而我们试图自己去做优化的话,又会因为跟当前的架构设计相违背而困难重重。

另外一个感受是 Chromium 的代码变动非常频繁,幅度也很大。Chromium 除了一些新功能和特性外,还有大量的架构优化,性能优化等项目在同步进行中,给我的感觉是,这帮牛人真是太闲了,整天想搞个大新闻出来... 这导致的结果就是 Chromium 的代码变动非常频繁,而且每次架构/性能优化带来的代码变动幅度又很大。以 WebView 的合成器为例子,Android 4.4 WebView 是基于 M33,当时还是使用单合成器的架构,相对来说比较简单,Android 5.0 WebView 是基于 M37,改成了父子合成器的级联合成器架构,比 M33 时就复杂很多了,而当前主干上的 M42 的代码,又会引入独立的 GPU 线程,意味着父子合成器将会使用不同的 GPU 线程和 GL 上下文,架构就变得更复杂了。对整个渲染来说,还有已经进入主干正在逐步完善的 GPU 光栅化,和开始逐步进入主干的 Slimming Painting... 对研究 Chromium 代码的人来说,几个版本下来,搞不好就变得面目全非了。有时更让人郁闷的是,一些进入主干的特性可能还是实验性质,如果实际验证有问题,说不定下个版本就回滚了,比如 Android 4.4 WebView 使用的 GpuMemoryBuffer 因为兼容性问题在 5.0 时就回滚了...

最后还想谈一下对退化的理解。对于研究 Android WebView 这个平台的 Chromium 代码同学来说,对退化现象的理解是十分重要的,可能的退化现象包括:

  1. 某个单进程版本的对象是多进程版本的一个退化,比如 InProcessCommandBuffer,虽然其主要的部分已经专门适配了单进程模式,但是在很多其它地方还保留了多进程的模式;
  2. 虽然是多进程的设计,但是实际运行时是单进程的退化模式,比如 Content —— Host and Renderer;
  3. 虽然是多线程的设计,但是实际运行时是单线程的退化模式,比如父合成器实际就运行在单线程模式上;
  4. 某个模块的实例是完整实例的一个退化后特定用途的实例,比如父合成器就是完整合成器实例的一个特定用途的退化实例,仅仅用于完成很简单的特定工作;

理解这些退化现象,会帮助我们:

  1. 能够帮助我们更好地理解 Chromium 的设计,因为退化现象的存在,从实际运行时的场景来倒推原始的设计意图会存在一定困难,我们必须自行脑补一个反退化的过程,这样才能真正理解真实的设计意图;
  2. 因为理解了真实的设计意图,从而能够帮助我们编写出符合 Chromium 设计的代码,否则容易编写出实际运行 OK,但是不符合 Chromium 设计,未来无法维护的代码;
  3. 能够帮助我们找到一些可能的优化点,退化现象的存在意味这实际上不是符合真实运行环境的最佳设计,存在一定的优化空间,比如 Content IPC 是否能够使用线程消息通信来取代,当然实际优化时,必须考虑优化的难度,未来维护的成本和可能的收益,只有收益足够大的时候才应该去做;
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注