[博客翻译]到目前为止,我们与Swift的旅程——一些笔记和思考


原文地址:https://forums.swift.org/t/our-journey-with-swift-thus-far-some-notes-and-reflections/70510


背景

我们的团队正在默默开发一款新型电子交易系统,该系统基于Swift语言,适用于macOS和Linux平台。这一项目建立在我们过去25年间,使用C、Objective-C、C++、Java、C#以及一段时间内的自研DSL语言完成的两个项目的经验之上。这些项目规模庞大,代码量超过2500万行,涉及数百个组件,团队规模从40人到100多人不等。

这些系统在服务器端是高性能的分布式集群系统,并配备了复杂的前端,实现了数据可视化和监控。服务器端提供API接口,允许客户插入自定义插件以执行例如交易活动或定价等操作,通常需要在进程中运行(即动态加载)。这一功能已经通过C++、Java、C语言以及自研DSL实现。当我们提到“服务器端”时,我们指的是传统意义上具有高性能IPC等功能的服务器,而不是通常的HTTP、JSON等。

深入研究Swift的生态系统,我们获得了宝贵的见解,包括它的优势和需要改进的领域。我们分享的反馈基于我们迄今为止的旅程和遇到的挑战,旨在为Swift的持续完善提供建设性的思考。在我们正式启动之前还有一段路要走,但我们希望利用这个时机分享一些我们的反思和思考。

3.png

为什么选择Swift?

我们的愿景是,下一代系统不仅使用一种表达性强、内存安全且高性能的语言,而且我们应该能够从上到下使用单一技术栈。这样能够实现出色的代码复用,工程师可以更容易地在不同的层面上接手工作,并能够更容易地从客户端和服务器端的统一类型实现中受益。此外,我们也有商业必要在服务器端部署Linux,除了用于可视化的macOS(可能还会支持更多的苹果平台)。

Swift勾勒出了这些需求,同时还提供了使用SwiftUI创建原生、良好用户界面的能力。尽管有其他一些选项,但由于各种原因,它们没有满足我们的标准(与本文无关)。

优点

整体设计

Swift的设计理念是“默认安全”——严格的类型检查一开始可能有些挫败感(好吧,有时候还是会有;-) ),但克服了最初的小障碍后,我们确实看到了它的回报——代码在通过编译器后,通常能够立即按预期工作(特别是与C风格的语言相比)。在Swift中,大规模重构工作通常更快、更稳健,这对我们来说非常有价值。运行时错误的数量只是传统工具常见错误的一小部分。我们对这一方面感到非常满意。即将到来的静态数据竞争安全保证就是锦上添花了。

语言的表现力总体很好,我们认为整体哲学有一系列良好的权衡。我们有点担心语言的复杂性对新手来说会继续增长,但希望渐进式披露的理念能够随着时间的推移使其变得可管理。

过去几年中,主要语言特性的发展速度很快,有几个特性正好符合我们的需求,对我们非常有用,例如concurrency/Async+Await/Actors,Distributed Actors、内存所有权(超级重要,很高兴它正在被开发!),以及对泛型和宏的整体改进。

开放式的演进过程总体上非常好(尽管像结果构建器这样的小问题除外)。能够看到路线图真的很有帮助,我们非常感激——例如,我们看到分布式演员和新的谓词类型正在开发中,我们可以选择在内部简单地存根实现,然后在它们发布时转移过去——非常好。

ABI稳定性真的是一个关键特性——我们确实想要为我们面向客户的SDK使用库演进,这是一个很棒的特性——不仅对苹果这样的系统提供商有用,对我们这样的第三方也是如此。我们需要能够发布客户可以链接的共享库,以便我们可以动态加载具有弹性和类型兼容性的插件。

生态系统

我们发现可用于服务器端开发的包生态系统比预期的要广泛——随着时间的推移,我们也帮助贡献了一些我们缺少的部分,例如HDR直方图、swift-kafka-client和性能基准测试。

我们对Foundation的本地重写也非常满意(并且已经开始大量使用某些新特性,如谓词),并且非常期待它作为Linux内置实现的整合——它将我们从无Foundation政策转变为现在的采用它。

SwiftPM插件(和宏)正在启用更广泛的工作流程。

总的来说,我们对语言本身非常满意,并看到了随着时间的推移的显著进步。

主要挑战

许多问题都集中在更丰富的动态库支持、API打包和Linux平台支持上,这是我们为了正确打包产品所需要的。

Linux上具有库演进的动态库支持——即使是有明确限制的官方支持包也会很有用(因为我们的客户将拥有完全控制的环境,可以强制要求特定的库版本和操作系统等的补丁级别)。

用于工件包的库支持(我们目前正在使用一个支持Linux上的xcframeworks的自定义工具链来解决它,但这不是长期可持续的)。

并发运行时分析工具可以更丰富,尤其是对任务——虽然我们在Instruments中有一些工具,但如果能够在LLDB中访问类似的任务信息(异步任务堆栈回溯等,用于创建/运行/休眠),包括检查任务本地信息,那将是非常棒的(尤其是在缺乏Instruments的Linux上……)。

第三方API的编译和运行时可用性检查支持目前还不可用。

动态库作为一等公民的基本支持,例如目标不能动态链接。

次要挑战

并发和测试

工具和平台支持

Linux支持有时落后(例如宏支持),这推迟了我们对新版本的采用,而且并不总是透明的。

当使用新的Foundation时,Linux上的构建时间相当痛苦(我们不仅需要构建swift-foundation,还需要由于使用宏而构建swift-syntax——这当然是一个众所周知的问题,但我还是想提一下,因为它导致了我们长时间的CI周转)。

构建稳定性和性能——我们经常需要清除.build或SwiftPM缓存或Xcode的派生数据,因为会出现奇怪的问题(比如Xcode中缺少的包),例如,似乎比预期重建得更多(例如这个示例7的基准测试)。

SwiftPM的产品/目标模型对于较大项目来说有点问题;目标暴露给下游项目,目标不能依赖同一包中的产品,目标名与模块名的冲突(别名并不总是有效)。

依赖项的本地开发有点笨拙,Swift包编辑只部分解决了这个问题。我们最终得到了一个有时复杂且不理想的项目结构,并在其上使用了一些工具/解决方案。

LLDB支持在Linux上尤其是崩溃多(并且报告的问题很少更新),不过随着时间的推移正在改善。

Libdispatch作为线程管理的基础(在Linux上)继续滞后于macOS实现(假设主要是由于合并更新实现的难度,因为libdispatch大量使用Mach特性)。也许在某个时候应该/可以重新考虑与线程管理的集成方法,但可以理解,这里有很多历史考虑因素,因为有很多代码使用Dispatch。

性能

性能问题有时很难诊断和解决(例如不必要的复制,我们在使用存在性/泛型时可能会遇到大量的元数据查找,如果没有得到正确的咒语,就会有大量的ARC流量等)——需要允许编译器适当专业化的解决方案。感觉像是Swift中分析反模式的一种工具可以帮助,我们很乐意讨论细节。

泛型虽然随着时间的推移有所改进,但仍然不是没有摩擦的,有时要获得期望的行为相当复杂。从性能的角度来看,这有点碰运气。保证事物得到专业化是具有挑战性的,很容易进行很好的泛型重构,只是跑到你意想不到的性能悬崖上。在这一领域的改进将是很棒的,例如,已经有关于各种注释的讨论(例如保证专业化)。

KeyPath性能是个问题——不幸的是,KeyPath的性能迫使我们即使在像排序比较器这样的简单情况下也要进行变通,因为它们会完全占据样本——如果它们能够无所畏惧地使用就好了。

在使用泛型和面向协议的编程时,需要更深入的文档和性能最佳实践提示(与前一点相关)。基本上是对旧的性能最佳实践的后续,但更多地关注模块间的考虑。

分析程序行为缺少一些运行时钩子,以更好地理解运行时行为和进行更好的基准测试。

总的来说,我们欢迎在运行时性能、编译时间和整体稳健性(SwiftPM / 工具链 / Linux支持)方面进一步投资。

结语

总之,我们对Swift的经验大体上是积极的,因为它的现代特性和强大的生态系统,也突出了可以进一步完善和扩展其能力的领域,尤其是对于稍微复杂和高级的开发场景。我们希望这些反馈能作为Swift持续发展和改进的建设性贡献,反映了我们推动生态系统积极发展的承诺。我们很乐意详细讨论并参与上述任何主题的讨论。

非常感谢所有参与Swift及相关包开发的人员——我们认为它正处于一个很好的轨道上,期待继续我们的旅程!

特别感谢所有在这些论坛上参与和分享知识的人,这是一个宝贵的社区资源,谢谢!

Joakim