Chromium 代码研究的一些感想
自己研究Chromium的源代码(尤其是Android WebView这块技术),已有不少时间致力于探索与实践相关技术,并将自己的点滴思考与体会整理出来。如果能对他人提供一些参考价值的话,也算积累了一些经验与收获。
在研究过程中深刻体会到了架构设计的重要性。该开源项目特别注重其体系结构的模块化与可扩展性。各个模块内部实现了高度内聚,并且每个模块都实现了高度独立运行。此外,在功能划分上还采取了"功能划分-独立运行-分层协作"的设计理念,并通过将功能划分成独立的功能块来实现各部分之间的协作关系。对外提供的API非常直观,并且能够满足不同场景的需求。同时还需要注意的是那些需要用户自行实现具体逻辑的部分以及那些需要用户自行实现具体逻辑的部分以及那些需要用户自行提供实现细节的部分也需要特别关注。
总体而言,在多个关键性的非功能性需求中,
- 架构的合理性和灵活性
- 安全性
- 性能
- 资源占用(CPU/GPU/内存)
具有高度内聚性的独立组件,在仿真的测试环境中支持模块化测试和单元测试功能,并能在运行时根据需求灵活配置系统参数以确保架构设计的可扩展性和适应性。该系统通过这种方式确保在多种不同平台上运行顺畅(从单个 WebView 组件到完整的浏览器系统)。
乍观之,这些似乎都具备优势。
然而,在研究Chromium的过程中不难发现,
系统的灵活性带来了更高的复杂性。
系统层次有所提升,
分析实际运行流程变得更为复杂。
为了实现目标,
我们需要面对大量中间层架构,
众多接口设计以及多样化的实现方式。
这些因素叠加在一起,
给研究工作带来了额外的压力。
值得注意的是,
对于专注于Android WebView的研究者而言,
这种状况显得尤为不公平。
实际上,
虽然Android WebView架构相对简单,
但它不得不承担其他平台带来的 added complexity.
这种状态在之前的分享中屡见不鲜,
对于研究Chromium Android WebView代码的专业人员而言,
当前的状态实则不尽如人意。
仅就复杂度增加而言或许不足为道。然而,在灵活性与复杂性之间确实容易出现资源消耗急剧上升的情况(CPU/GPU/内存),尤其是在移动设备这种资源受限的环境中更加明显——即使在优化后也可能难以达到理想状态)。评估 Chromium 性能如何恰到好处地是一个有趣的问题——一方面 Chromium 的表现并不逊色(特别是在Blink内核在JS/DOM/CSS等方面的优秀表现上),但从另一个角度来说其各方面性能指标也相当均衡(没有明显的劣势),能够良好地适应各种网页类型的需求;但另一方面 Chromium 作为一个复杂的浏览器框架确实需要投入大量资源来实现多线程分解与高并发处理——这种做法虽然有助于提升性能但也必然会导致进一步的资源消耗上升(具体表现为CPU/GPU/内存等指标的增长)。因此,在实际应用中很难完全实现预期效果这一问题的确切程度仍需进一步探讨
Chromium的内存资源消耗是一个较为突出的问题,在这种情况下,请考虑对比Android 4.3版本中的非Chromium基于WebVIEW的应用程序与Android 5.0版本中的WebVIEW组件。在Android 5.0版本中,每个硬件合成器都独立维护着各自的图像渲染缓存区和纹理缓存池。这意味着当多个WebView共享同一个视图层次结构时(即使只有一个显示中),仍然会占用着丰富的图像渲染资源和纹理缓存空间。而在Android 4.3版本中,则采用了一个共享式的设计模式:多个WebView共享同一个缓存池,在这种架构下能够维持更高的使用效率,并且不会像预期那样随着WebView数量的增加而发生线性比例的增长(因为共享机制允许非活跃的WebView将存储的空间转移给当前活跃的组件)。Chromium的设计理念是为了实现各个硬件合成器之间的更加独立与自给自足状态,在此基础之上实现了更为灵活的渲染架构以适应不同的系统配置需求;这种设计理念使得官方团队难以对现有架构进行优化改进;而我们团队则试图通过自主设计来提升性能表现;但受限于当前系统的架构限制与技术实现细节;我们目前的努力往往无法达到预期的效果提升目标
另一个感受是Chromium的代码变更频率较高且规模也相当大。它不仅推出了新的功能与特性,还有大量的架构优化与性能优化项目正在同步推进中,在我的眼中这无异于白日做梦——光是想着能搞出个新花样就已让我觉得心力交瘁了。这直接导致其代码变更频率较高且每次架构/性能优化所引发的代码变动幅度都相当可观。以Android 4.4 WebView为例,在M33的基础上采用了单一合成器架构相对较为简单;到了Android 5.0时改用父子合成器的级联合成器架构(M37),较之前有了质的飞跃但复杂度也随之倍增;如今在主干上的M42代码更是引入了独立的GPU线程这一变化使得父子合成器将不得不面对各自拥有不同的GPU线程以及GL上下文这无疑使整个渲染流程变得更加复杂化了。此外在主干上已经开始逐步完善的一系列GPU光栅化工作以及正在进行中的精简绘制(Slimming Painting)技术也在不断推进着这项工程的发展进程。对于研究Chromium内核的人来说每当经过几个版本更新后几乎都会发现原有的代码框架已经发生了翻天覆地的变化有时甚至会让人感到不知所措——有些刚进入主干的功能还处于实验阶段若验证过程中出现问题很可能会在后续版本中被回滚掉例如像之前提到的Android 4.4 WebView中的GpuMemoryBuffer因兼容性问题在5.0版本就被移除了。
最后还想进一步阐述我对退化的认识。就该领域而言,在研究 Android WebView 这个平台的 Chromium 代码方面,认识和分析退化现象的重要性不言而喻。常见的退化类型包括:
- 该单进程中对象可视为多进程中一种优化延伸,在实例中如InProcessCommandBuffer所示,
虽然主要功能已专为单进程中设计,
在其他方面仍保留了多进程中特性; - 尽管最初设计为一个多进程中架构,
实际运行中却采用单一进程中Host和Renderer组件组合完成任务; - 该模块虽原定采用多线程设计,
在执行过程中却因特殊需求而被简化为单一线程运作; - 该模块是一个完整模块在特定需求下的特化版本
理解这些退化现象,会帮助我们:
- 能够帮助我们更好地理解 Chromium 的设计,因为退化现象的存在,从实际运行时的场景来倒推原始的设计意图会存在一定困难,我们必须自行脑补一个反退化的过程,这样才能真正理解真实的设计意图;
- 因为理解了真实的设计意图,从而能够帮助我们编写出符合 Chromium 设计的代码,否则容易编写出实际运行 OK,但是不符合 Chromium 设计,未来无法维护的代码;
- 能够帮助我们找到一些可能的优化点,退化现象的存在意味这实际上不是符合真实运行环境的最佳设计,存在一定的优化空间,比如 Content IPC 是否能够使用线程消息通信来取代,当然实际优化时,必须考虑优化的难度,未来维护的成本和可能的收益,只有收益足够大的时候才应该去做;
