字节跳动DanceCC工具链系列之Swi

前言DanceCC(DanceCompilerCollection)是字节跳动的终端技术团队(ClientInfrastructure)下的编译工具链品牌,编译工具链团队成员由国内和硅谷两地的编译器专家及构建系统专家组成,提供基于开源的LLVM/Swift项目深度定制的clang/swift编译器、链接器、lldb调试器和语言基础库等工具及优化方案,覆盖构建性能优化及应用性能稳定性优化等场景,本系列将会围绕这些场景中的优化案例,介绍编译工具链技术在字节的优化方案和落地情况。背景通常来说,大型Swift项目常含有大量混编(Objc/C/C++甚至是Rust)代码,含有超过个以上的SwiftModule,并可能同时包含二进制部分和源码部分。而这种大型项目在目前的Xcode13体验下非常不好,经常存在类似“断点陷入后变量面板卡顿转菊花”、“显示变量失效”等问题。而且一直存在于多个历史Xcode版本。

图1:Xcode变量区显示卡顿转菊花,测试使用Xcode13.3和下文提到的复现Demo

这部分AppleTeam迟迟不优化的原因在于,Apple公司的内部项目和外部项目开发模式的巨大差异。Apple内部产品,如系统应用,系统库,会直接内嵌到iOS固件中,并直接受益于dyldsharedcache(参考WWDC-AppStartupTime:Past,Present,andFuture[1])来提升加载速度。这意味着他们通常会将一个App,拆分为一个薄的主二进制,搭载以相当多的动态链接库(DynamicFramework),以及插件(PlugIn)的模式来进行开发。举个例子,我们以iOS的消息App(MobileSMS.app)为例子,使用iOS15.4模拟器测试。可以看到其主二进制大小仅有KB(x86_64架构)。通过otool-L查询链接,可以看到总计动态链接了22个动态链接库,其中有9个是非公开的,大都是支撑消息App的功能库,这些库占据了大量存储。图2:消息App的动态链接库列表

而iOS平台的第三方开发者的工程,为了追求更快的冷启动时长,由于没有了dyldsharedcache的优化(dyld3提出的启动闭包只能优化非冷启动),很多项目会使用尽量少的动态链接库。加之开源社区的CocoaPods,Carthage,SwiftPM等包管理器的盛行导致的SwiftModule爆炸增长,预二进制的Framework/XCFramework包装格式的滥用,加之闭源三方公司的SDK的集成,最终形成了一个无论是体积还是符号量都非常巨大的主二进制,以及相当长的SearchPaths。

以公司内飞书应用的内测版为例子,在使用Debug,Onone模式编译,不剥离(Strip)任何符号情况下,可以看到其主二进制大小为1.1GB,动态链接库数量为,但是仅包含Apple的系统库和Swift标准库。业务代码以静态链接库集成。

图3:公司飞书应用的动态链接库列表

上述这两种不同的工程结构,带来了非常显著的调试体验的差异,并且Apple公司近年来的XcodeTeam和DebuggerTeam优化,并没有完全考虑部分第三方开发者常使用的,厚主二进制下的工程结构。

PS:理论上可以通过业务的工程结构的改造,在本地开发模式下,使用一个动态链接库包裹基础静态链接库的方式,减少主二进制大小(也会减少后续提到的DWARF搜索的耗时),但是大型项目推进工程结构的改造会是一个非常漫长的过程。

图4:一种减少主二进制大小的工程结构设计

解决方案:自定义LLDB工具链

经过调研,我们发现业界常见做法,无外乎这几种思路:

工程改造:缩减SwiftModule/SearchPath数量:可行,但是收益较低,且不可能无限制缩减

通过LLDB一些开关:可行,但是内网测试下依旧达不到理想的调试状态

我们致力于在字节跳动的移动端提供基础能力支持,因此提出了一套解决方案,不依赖业务工程结构的改造,而是从LLDB工具链上入手,提供定向的调试性能优化。

调研期间也确认到,借助自定义LLDB工具链,集成到XcodeIDE是完全可行的,包括iPhone模拟器、真机以及Mac应用。

图5:自定义LLDB工具链的文件结构,系列后续文章会单独讲解,这里不展开

而LLVM/LLDB本身的工具链代码,在Apple的开源范畴之内(仓库


转载请注明:http://www.aierlanlan.com/cyrz/298.html

  • 上一篇文章:
  •   
  • 下一篇文章: