在之前的一篇文章《看我是如何用C编写一个小于8KB的贪吃蛇游戏》中,介绍了在。NETCore3。0的环境下如何将贪吃蛇游戏降低到8KB。不过也有很多小伙伴提出了一些疑问和看法,主要是下面这几个方面: 。NETCore3。0可以做到这么小,那么。NET7表现会不会更好? 不敢在生产中用这样的方式,我看CoreRT这个仓库我看已经归档了。 这样子弄太麻烦了,有没有更简单的办法? 今天笔者就给大家一一解答这些问题。。NET7下的贪吃蛇游戏 我们知道在。NET7中已经发布了NativeAOT正式的支持,经过。NET5、。NET6的迭代,NativeAOT已经基本成熟可用,那么在。NET7中重新编译这个游戏,有没有什么进步呢?让我们来看一看。 有外网条件的朋友可以看下方的这个GITHUB链接的代码,这个代码就是提交了升级。NET7NativeAOT的实现:https:github。comMichalStrehovskySeeSharpSnakepull24使用。NET7单文件发布为了达到我们的目的,对于这个项目的csproj文件需要有一些小的改动。首先就是将对应的TargetFramework修改为net7。0版本。 此时就已经完成。NETCore3。1到NET7。0的迁移了,我们运行下面的命令,可以获得一个65MB大小的程序,这个和之前。NETCore3。1没有什么区别。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasespan开启ILLinker 另外后面的。NET版本支持更好的程序集剪裁,也就是ILLinker工具,我们运行命令行时p:PublishTrimmedtrue选项就可以启用。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasep:PublishTrimmedspanstyleboxsizing:borderbox;spantruespan此时我们可以发现,只有11MB大小了,比。NETCore3。0时代的25MB降低了一半多。使用NativeAOT功能然后我们就要开始使用。NET7的NativeAOT功能,需要在项目文件中加入PublishAottruePublishAot选项。我们加入了一个条件,在平时不开启,只有输入不同Mode的时候才开启。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasep:Modespanstyleboxsizing:borderbox;spanNativeAOTspan此时可以获得一个2。86MB大小的程序,比。NETCore3。0时代的4。7MB要小了快一半。使用Moderate模式继续修改csproj文件,让它支持Moderate模式,也就是使用IlcGenerateCompleteTypeMetadatafalseIlcGenerateCompleteTypeMetadata不生成完整的类型元数据,另外也用IlcOptimizationPreferenceSizeIlcOptimizationPreference让编译器为程序大小进行优化,而不是速度。由于后面的模式也需要支持这个,所以加入了很多条件编译的选项。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasep:Modespanstyleboxsizing:borderbox;spanNativeAOTModeratespan结果和上面的一样的2。86MB,也就是说现在NativeAOT应该默认就是Moderate模式。进一步移除无关数据 接下来我们进一步移除无关的数据。 使用EventSourceSupportfalseEventSourceSupport关闭对EventSource的支持 使用UseSystemResourceKeystrueUseSystemResourceKeys删除System。程序集的异常消息。 使用InvariantGlobalizationtrueInvariantGlobalization删除全球化特定的代码和数据。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasep:Modespanstyleboxsizing:borderbox;spanNativeAOTHighspan此时我们再次发布,可以看到大小已经降低到了2。15MB,比。NETCore3。0时的3。0MB降低了快30。继续移除无关数据 通过IlcGenerateStackTraceDatafalseIlcGenerateStackTraceData移除堆栈跟踪数据 通过IlcInvariantGlobalizationtrueIlcInvariantGlobalization移除其它语言的支持 通过IlcFoldIdenticalMethodBodiestrueIlcFoldIdenticalMethodBodies将相同的方法体进行合并。只为我们省下了几百KB,此时大小来到了1。88MB。关闭反射接下来我们可以继续使用IlcDisableReflectiontrueIlcDisableReflection来关闭反射,移除掉一些反射的元数据。spanstyleboxsizing:borderbox;display:block;lineheight:1。3;dotnetpublishspanstyleboxsizing:borderbox;rspanwinx64spanstyleboxsizing:borderbox;cspanReleasep:Modespanstyleboxsizing:borderbox;spanNativeAOTReflectionFreespan关闭反射后,大小来到了1。21MB,这应该是不用骚操作能达到的最小大小了。和。NETCore3。0的对比 下图是。NET7和。NETCore3。0在不同模式下大小的对比,可以看到经过。NET5。0、。NET6。0的发展,NativeAOT变得更加成熟了。模式。NETCore3。0。NET7。0幅度单文件发布65MB65MB0ILLinker剪裁25MB11MB56NativeAOT4。7MB2。86MB40NativeAOTHigh3。0MB1。88MB38关闭反射1。21MB1。21MB0关于CoreRT 在博客园的评论中,看到有一位朋友留言,说不敢在生产环境中使用,而且CoreRT已经归档。其实大可放心的使用,CoreRT关闭的原因也正如下面链接仓库里面说的一样,是代码已经合并到runtimelabnativeaot项目中。https:github。comdotnetcorert 而NativeAOT已经从实验室中毕业,合并到dotnetruntime中了,也就是。NET7看到的PublishAot选项,可以关注下面的微软文档。https:learn。microsoft。comzhcndotnetcoredeployingnativeaotNoRuntime用起来很折腾 另外看到评论区大家吐槽的点就是后面那些骚操作看起来很麻烦,有没有更简单的方式?这个其实是有的,上篇文章的作者推出了bflat这个项目。 bflat是Roslyn(生成。NET可执行文件的官方C编译器)和NativeAOT(neCoreRT)的混合物,NativeAOT(neCoreRT)是基于CoreCLR的。NET的提前编译器。因此,您可以使用高性能CoreCLRGC和本机代码生成器(RyuJIT)访问最新的C功能。 bflat将两个组件合并到一个用于C的提前交叉编译器和运行时中。bflat目前可以针对: x64arm64基于glibc的Linux(x64(CentOS7)上为2。17或更高版本,arm64(Ubuntu18。04)上为2。27或更高版本) arm64基于bionic的Linux(AndroidAPI级别21) x64arm64Windows(Windows7或更高版本) x64UEFI(仅适用于stdlib:zero) 对基于musl的Linux的支持正在开发中。bflat可以生成本机可执行文件,也可以生成可通过FFI从其他语言调用的本机共享库,下面是它的开源地址:https:github。combflattenedbflat 使用NoRuntime模式最小可以做到4KB大小,而且支持无操作系统裸机UEFI启动。总结 我们可以惊喜的看到NativeAOT经过几年的发展已经逐步走向成熟,另外还有裸机可运行的C程序,这给了我们很多的想象空间,可能有那么一天C程序会运行在只有几百KB内存的物联网终端设备上,UEFI启动程序使用C编写等等。