当前位置: 首页 > 工具软件 > CoreCLR > 使用案例 >

Unity的未来,是固守Mono,还是拥抱CoreCLR?

宓英哲
2023-12-01

TLDR;

Unity坚定的拥抱.NET标准生态,正全速向CoreCLR迁移。

Mono vs CoreCLR

对于一个C#的初学者,首先要了解的便是.NET和C#的关系。所以这里不再赘述。对于一个Unity的初学者,在使用C#编码的过程中,一定会遇到一些C#新特性不能在项目中使用的情况,这是因为微软官方提供的.NET运行时环境(最新版为 .NET 6 的 CoreCLR)远比Unity集成的Mono强大。由于历史原因,Unity一直未能使用最新的.NET运行时。本文就来细说一下其中的历史,以及Unity未来的发展。

首先,Mono是.NET的开源实现,由Xamarin牵头维护 mono/mono 这个repo。2016年Xamarin被微软收购,将其license从GPL改成了MIT,同时微软也参与到Mono的开发中。在微软的 dotnet/runtime 这个repo中,可以发现mono,但这并不是mono/mono的替代品,而是微软为了方便其他组件开发,将部分代码拷贝过来进行魔改,在需要的时候同步回mono/mono。(出处:Announcement: Consolidating .NET)

然后说一下 CoreCLR。微软改名部绝非浪得虚名,这些年的一系列改名操作把好端端的.NET技术搅成一滩浑水。非常简要的说,过去.NET运行时只能用于Windows平台,名为 .NET Framework,运行时叫CLR,大名鼎鼎的《CLR via C#》就是基于该运行时。后来微软决定将.NET技术开源,并彻底地跨平台(Windows,Linux,MacOS等),顺便大幅提高运行效率并抛弃一些旧组件,重写了一版 .NET Core,运行时叫 CoreCLR。双线开发并不是个好主意,因此再后来微软决定将前者废弃,以 .NET Core相关技术为核心,将.NET技术大一统(桌面开发、移动开发、游戏开发、IoT开发、云开发等等),史称 .NET 5。因此,往后.NET的官方运行时就叫CoreCLR。repo在这里:dotnet/runtime。

大一统的饼是画出来了,但当下移动平台的.NET开发的主流还是Mono。这里结合Wiki,对Mono的重要历史加以整理。

  • 2010年9月,Mono2.8发布,带来了新的分代式GC:SGen;支持 C#4;支持 .NET 4.0。

  • 2013年7月,Mono3.2发布,SGen取代Boehm成为默认的GC。

  • 2015年4月,Mono4.0发布,开始集成 .NET Core;支持 C#6。

  • 2017年5月,Mono5.0发布,开启了concurrent SGen;使用Roslyn编译器;支持 C#7。

  • 2019年9月,Mono6.4发布,支持 .NET Standard 2.1;支持 .NET 4.8。

然而Mono对.NET的特性支持和性能一直落后于微软官方的CLR,性能上也大幅落后于CoreCLR(补充:2018年Unity的官方数据是CLR比Mono快30%-3倍。Mono这些年的进步有目共睹,但奈何不了微软爸爸的钞能力给CoreCLR研发上的加成。经过4年的发展,一些民间测试资料已显示CoreCLR比Mono快10-20倍,比IL2CPP快8-10倍)。

过去,Unity 选择 Mono

上文理清了Mono 和CoreCLR的关系,下面说说Unity在这二者间的选择。

早在2008年,Unity就宣布和Mono合作,但后续Mono新版本使用SGen GC取代Boehm GC时,Unity不想再次付许可证费用。直到2021年7月,Unity依然依赖 Boehm GC。这是一种没有分代的(扫描慢)、Mark-Sweep的(会有内存碎片问题)、保守(不能精确地识别垃圾)的GC。

Unity has been and is still relying on the Boehm GC, which is a conservative (stack-root) GC. The link above doesn't go into some details like how managed objects on the stack are collected by the GC, but basically: a conservative GC will scan the entire stack of all managed threads to "pin" memory referenced by it. Because of this blind scan, it can bring false pin, because it can interpret an integer value, as a pointer to a region of the heap memory, while it was really an int in the first place. By doing so, a conservative GC can start to block some objects from being collected (or worse for a moving-generational GC, to relocate objects). Otoh, a precise GC is able to scan precisely stack-roots and report only pointers that actually point to heap memory. In order for a precise GC to work, the (JIT) codegen needs to be GC aware, which is the case for CoreCLR.

对于Boehm GC造成的性能问题,Unity官方有一些折中方案。

  • 先尽量分配好所有对象的内存,然后关闭GC,等到合适的时机(如关卡结束),再开启GC;

  • 默认开启 incremental 模式分帧处理,注意如果在期间有大量引用关系的改写,分帧处理反而会有大量额外性能损耗(主要来自写屏障)

未来,Unity 选择 CoreCLR

自2016年微软将Mono的许可证由GPL改为MIT以来,Unity也加入了 .NET Foundation,开始将最新的Mono集成到自己的引擎中。但随着微软构筑开源的大一统解决方案 .NET 5,Unity似乎改变了原先的想法。从官方论坛中可以总结出他们的规划:

  1. 首先集成最新的Mono,因为其支持 .NET Core 的BCL;

  2. 然后将自家的 IL2CPP 也更新(其依赖Mono的输出结果);

  3. Unity 2021.2开始完全支持.NET Standard 2.1,C#8和部分C#9(Span,Range,default interface methods),其中Span的影响非常深远,目前和BurstCompiler的NativeArray还不能无缝转换,最大难点是Span并没有自己的memory但后者有;

  4. 支持 C#9/10,基于前面的工作,这一步并不难;

  5. 支持 .NET 6(跳过 .NET 5)。但有两大难题:

  • 所有dll必须重新编译;

  • 要修改 UnityEditor 中大量使用 AppDomain进行hot reload的部分(AppDomain在新版.NET中几乎被废弃,出处);目前的替代类 AssemblyLoadContext 并不能提供之前 AppDomain Reload的所有功能。

In general Assembly Load Context is cooperative, and any remaining references (static fields, GC Handles, running threads, etc) will prevent the code from being unloaded.

6. 用CoreCLR替代Mono(GC也相应升级为CoreCLR GC),在此之前,GC并不会升级为Mono的SGen。这项工作会持续比较久,目前还没有ETA。Unity大部分代码是C++,C#只有薄薄的一层(但是越来越多的代码在切换到 C#)。在切换到CoreCLR后,其访问Managed Object的方式需要彻底改变,因此改动会很大。总体顺序是:先将Player替换为CoreCLR,然后将Editor也替换掉。

长远来看,Unity该团队已经意识到自己当年的一些轮子(Coroutine, Customized Boehm GC, IL2CPP, asmdef等)在近几年来.NET运行时、工具链和整套生态的飞速发展面前显得有些陈旧,正在致力于向开发者提供原汁原味的.NET开发体验(出处),同时尽量不颠覆原有的使用习惯(例如出于这些原因,UPM不会和NuGet双向互通)。

另外,Unity团队还有很多高优先级的feature要做,希望Unity越来越好吧。最新消息可关注官方论坛的讨论:

Unity Future .NET Development Status - Unity Forumforum.unity.com/threads/unity-future-net-development-status.1092205/

另外,文中提到的《CLR via C#》虽然内容基于 .NET Framework,但由于大部分内容在 .NET5+ 中并没有变化,该书凭借其内容深入和广度,依然是 C# 进阶学习的必读经典。

 类似资料: