在之前的一篇文章《看我是如何用C#编写一个小于8KB的贪吃蛇游戏》中,介绍了在.NET Core 3.0的环境下如何将贪吃蛇游戏降低到8KB。不过也有很多小伙伴提出了一些疑问和看法,主要是下面这几个方面:

  • .NET Core 3.0可以做到这么小,那么.NET7表现会不会更好?
  • 不敢在生产中用这样的方式,我看CoreRT这个仓库我看已经归档了。
  • 这样子弄太麻烦了,有没有更简单的办法?

今天笔者就给大家一一解答这些问题。

.NET7下的贪吃蛇游戏、

我们知道在.NET7中已经发布了NativeAOT正式的支持,经过.NET5、.NET6的迭代,NativeAOT已经基本成熟可用,那么在.NET7中重新编译这个游戏,有没有什么进步呢?让我们来看一看。

有外网条件的朋友可以看下方的这个GITHUB链接的代码,这个代码就是提交了升级.NET7 NativeAOT的实现:

https://github.com/MichalStrehovsky/SeeSharpSnake/pull/24

使用.NET7单文件发布

为了达到我们的目的,对于这个项目的csproj文件需要有一些小的改动。首先就是将对应的TargetFramework修改为net7.0版本。

此时就已经完成.NET Core 3.1到NET7.0的迁移了,我们运行下面的命令,可以获得一个65MB大小的程序,这个和之前.NET Core 3.1没有什么区别。

dotnet publish -r win-x64 -c Release

开启IL Linker

另外后面的.NET版本支持更好的程序集剪裁,也就是IL Linker工具,我们运行命令行时/p:PublishTrimmed=true选项就可以启用。

dotnet publish -r win-x64 -c Release /p:PublishTrimmed=true

此时我们可以发现,只有11MB大小了,比.NET Core 3.0时代的25MB降低了一半多。

使用NativeAOT功能

然后我们就要开始使用.NET7的NativeAOT功能,需要在项目文件中加入<PublishAot>true</PublishAot>选项。我们加入了一个条件,在平时不开启,只有输入不同Mode的时候才开启。

dotnet publish -r win-x64 -c Release /p:Mode=NativeAOT

此时可以获得一个2.86MB大小的程序,比.NET Core 3.0时代的4.7MB要小了快一半。

使用Moderate模式

继续修改csproj文件,让它支持Moderate模式,也就是使用<IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>不生成完整的类型元数据,另外也用<IlcOptimizationPreference>Size</IlcOptimizationPreference>让编译器为程序大小进行优化,而不是速度。由于后面的模式也需要支持这个,所以加入了很多条件编译的选项。

dotnet publish -r win-x64 -c Release /p:Mode=NativeAOT-Moderate

结果和上面的一样的2.86MB,也就是说现在NativeAOT应该默认就是Moderate模式。

进一步移除无关数据

接下来我们进一步移除无关的数据。

  • 使用<EventSourceSupport>false</EventSourceSupport>关闭对EventSource的支持
  • 使用<UseSystemResourceKeys>true</UseSystemResourceKeys>删除 System.* 程序集的异常消息。
  • 使用<InvariantGlobalization>true</InvariantGlobalization>删除全球化特定的代码和数据。

dotnet publish -r win-x64 -c Release /p:Mode=NativeAOT-High

此时我们再次发布,可以看到大小已经降低到了2.15MB,比.NET Core 3.0时的3.0MB降低了快30%。

继续移除无关数据

  • 通过<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>移除堆栈跟踪数据
  • 通过<IlcInvariantGlobalization>true</IlcInvariantGlobalization>移除其它语言的支持
  • 通过<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>将相同的方法体进行合并。

只为我们省下了几百KB,此时大小来到了1.88MB

关闭反射

接下来我们可以继续使用<IlcDisableReflection>true</IlcDisableReflection>来关闭反射,移除掉一些反射的元数据。

dotnet publish -r win-x64 -c Release /p:Mode=NativeAOT-ReflectionFree

关闭反射后,大小来到了1.21MB,这应该是不用骚操作能达到的最小大小了。

和.NET Core 3.0的对比

下图是.NET7和.NET Core 3.0在不同模式下大小的对比,可以看到经过.NET 5.0、.NET 6.0的发展,NativeAOT变得更加成熟了。

模式 .NET Core 3.0 .NET7.0 幅度
单文件发布 65MB 65MB 0%
IL Linker剪裁 25MB 11MB -56%
NativeAOT 4.7MB 2.86MB -40%
NativeAOT-High 3.0MB 1.88MB -38%
关闭反射 1.21MB 1.21MB 0%

关于CoreRT

在博客园的评论中,看到有一位朋友留言,说不敢在生产环境中使用,而且CoreRT已经归档。其实大可放心的使用,CoreRT关闭的原因也正如下面链接仓库里面说的一样,是代码已经合并到runtimelab/nativeaot项目中。

https://github.com/dotnet/corert

而NativeAOT已经从实验室中毕业,合并到dotnet/runtime中了,也就是.NET7看到的<PublishAot>选项,可以关注下面的微软文档。

https://learn.microsoft.com/zh-cn/dotnet/core/deploying/native-aot/

NoRuntime用起来很折腾

另外看到评论区大家吐槽的点就是后面那些骚操作看起来很麻烦,有没有更简单的方式?这个其实是有的,上篇文章的作者推出了bflat这个项目。

bflat是Roslyn(生成.NET可执行文件的"官方"C#编译器)和NativeAOT(née CoreRT)的混合物,NativeAOT(née CoreRT)是基于CoreCLR的.NET的提前编译器。因此,您可以使用高性能 CoreCLR GC 和本机代码生成器 (RyuJIT) 访问最新的 C# 功能。

bflat 将两个组件合并到一个用于 C# 的提前交叉编译器和运行时中。bflat目前可以针对:

  • x64/arm64 基于 glibc 的 Linux(x64 (~CentOS 7) 上为 2.17 或更高版本,arm64 (~Ubuntu 18.04)上为 2.27 或更高版本)
  • arm64 基于 bionic 的 Linux(Android API 级别 21)
  • x64/arm64 Windows (Windows 7 或更高版本)
  • x64 UEFI(仅适用于--stdlib:zero)

对基于 musl 的 Linux 的支持正在开发中。bflat 可以生成本机可执行文件,也可以生成可通过 FFI 从其他语言调用的本机共享库,下面是它的开源地址:

https://github.com/bflattened/bflat

使用NoRuntime模式最小可以做到4KB大小,而且支持无操作系统裸机UEFI启动。

总结

我们可以惊喜的看到NativeAOT经过几年的发展已经逐步走向成熟,另外还有裸机可运行的C#程序,这给了我们很多的想象空间,可能有那么一天C#程序会运行在只有几百KB内存的物联网终端设备上,UEFI启动程序使用C#编写等等。

.NET性能优化交流群

相信大家在开发中经常会遇到一些性能问题,苦于没有有效的工具去发现性能瓶颈,或者是发现瓶颈以后不知道该如何优化。之前一直有读者朋友询问有没有技术交流群,但是由于各种原因一直都没创建,现在很高兴的在这里宣布,我创建了一个专门交流.NET性能优化经验的群组,主题包括但不限于:

  • 如何找到.NET性能瓶颈,如使用APM、dotnet tools等工具
  • .NET框架底层原理的实现,如垃圾回收器、JIT等等
  • 如何编写高性能的.NET代码,哪些地方存在性能陷阱

希望能有更多志同道合朋友加入,分享一些工作中遇到的.NET性能问题和宝贵的性能分析优化经验。目前一群已满,现在开放二群,可以直接扫码进入。

如果提示已经达到200人,可以加我微信,我拉你进群: ls1075

另外也创建了QQ群,群号: 687779078,欢迎大家加入。

8KB的C#贪吃蛇游戏热点答疑和.NET7版本的更多相关文章

  1. Qt 学习之路 2(34):贪吃蛇游戏(4)

    Qt 学习之路 2(34):贪吃蛇游戏(4) 豆子 2012年12月30日 Qt 学习之路 2 73条评论 这将是我们这个稍大一些的示例程序的最后一部分.在本章中,我们将完成GameControlle ...

  2. Android快乐贪吃蛇游戏实战项目开发教程-01项目概述与目录

    一.项目简介 贪吃蛇是一个很经典的游戏,也很适合用来学习.本教程将和大家一起做一个Android版的贪吃蛇游戏. 我已经将做好的案例上传到了应用宝,无病毒.无广告,大家可以放心下载下来把玩一下.应用宝 ...

  3. 用C++实现的贪吃蛇游戏

    我是一个C++初学者,控制台实现了一个贪吃蛇游戏. 代码如下: //"贪吃蛇游戏"V1.0 //李国良于2016年12月29日编写完成 #include <iostream& ...

  4. WebGL实现HTML5的3D贪吃蛇游戏

    js1k.com收集了小于1k的javascript小例子,里面有很多很炫很酷的游戏和特效,今年规则又增加了新花样,传统的classic类型基础上又增加了WebGL类型,以及允许增加到2K的++类型, ...

  5. 100行JS实现HTML5的3D贪吃蛇游戏

    js1k.com收集了小于1k的javascript小例子,里面有很多很炫很酷的游戏和特效,今年规则又增加了新花样,传统的classic类型基础上又增加了WebGL类型,以及允许增加到2K的++类型, ...

  6. H5实现的可自定义贪吃蛇游戏

    原创游戏,使用lufylegend.js开发 用canvas实现的贪吃蛇游戏,与一般的贪吃蛇游戏不同,图片经过美工设计,代码设计支持扩展和自定义. 游戏元素丰富,包括障碍物(仙人掌),金币(奖励),苹 ...

  7. JS贪吃蛇游戏

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  8. 用OpenGL简单编写的一个最简单贪吃蛇游戏

    刚学OpenGL的时候,写的一个最简单的贪吃蛇游戏代码 如下: //贪吃蛇游戏 #include<stdio.h> #include<stdlib.h> #include< ...

  9. Java贪吃蛇游戏

    package snake.game; import java.awt.CardLayout; import java.awt.Color; import java.awt.Graphics;   i ...

  10. 「JavaScript」手起刀落-一起来写经典的贪吃蛇游戏

    回味 小时候玩的经典贪吃蛇游戏我们印象仍然深刻,谋划了几天,小时候喜欢玩的游戏,长大了终于有能力把他做出来(从来都没有通关过,不知道自己写的程序,是不是能通关了...),好了,闲话不多谈,先来看一下效 ...

随机推荐

  1. 支持JDK19虚拟线程的web框架,之一:体验

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于虚拟线程 随着JDK19 GA版本的发布,虚拟线程 ...

  2. F118校准(二)-- 操作步骤(使用PX01 PG点屏,并使用PX01 PG校准F118)

    1. 准备工作 硬件连接: CA310通过USB线材连接PC PX01通过USB线材连接PC F118通过灰排线连接PX01左上角的GPIO扩展口(如下图所示) LCM连接PX01 启动LcdTool ...

  3. .NET中的拦截器filter的使用

    拦截器的使用 使用场景分析 我们先想像一个场景,就是程序员开发软件,他是怎么工作的呢?我们都知道,普通的程序员只需要根据需求文档开发相应的功能即可,他不用和客户谈论软件需求,不用理会软件卖多少钱,他要 ...

  4. kubelet忽然不可用

    原因,有可能机器的cpu信息有变化(扩容或者缩容)解决办法: 删掉/opt/var/lib/kubelet目录下(或者/data/lib/kubelet)cpu_manager_state文件 然后m ...

  5. 1.WEB应用模式

    1. Web应用模式 在开发Web应用中,有两种应用模式: 前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的.] 前后端分离[把前端的界面效果(html,css,js分离到另一个服务 ...

  6. centos 7.6镜像_Centos7 配置本地yum源为iso镜像

    创建挂载路径 sudo mkdir /media/iso 挂载ISO镜像到目录 sudo mount -o loop CentOS-7-x86_64-Minimal-1810.iso /media/i ...

  7. Python基础之函数:3、多层语法糖、装饰器和装饰器修复技术及递归函数

    目录 一.多层语法糖 1.什么是多层语法糖: 2.多层语法糖用法: 二.有参装饰器 1.什么是有参装饰器: 2.有参装饰器的作用: 三.装饰器修复技术 1.什么是装饰器修复技术: 四.递归函数 1.什 ...

  8. java学习之注解

    0x00前言 1.注解是什么: (1)可以叫做注释类型,注解是一种引用数据类型,编译后也是生成class文件 (2)提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 比如 @Override ...

  9. JavaScrip基础学习笔记(一)

    一.三元表达式 1.1 什么是三元表达式 由三元运算符组成的式子我们称为三元表达式 1.2 语法结构 条件表达式 ? 表达式1 : 表达式2 1.3 执行思路 如果表达式为结果真 则返回表达式1的值, ...

  10. Jenkinsfile Pipeline 使用 SSH 连接

    前提 首先你需要将用到的 SSH 私钥保存到 Jenkins 的凭据中,这样你会获得一个 credentialId.这不是本文主要的内容,故不在此展开赘述,详情可参考官方文档:https://www. ...