试试将.NET7编译为WASM并在Docker上运行

之前有听到说Docker支持Wasmtime了,刚好.NET7也支持WASM,就带大家来了解一下这个东西,顺便试试它怎么样 。
因为WASM(WebAssembly) 一开始是一个给浏览器的技术,比起JS解释执行,WASM能用于提升浏览器的用户体验 , 因为在一些场景中它有着比JS更好的性能 。
大家可以将WASM理解为C#的MSIL或者Java的字节码,它并不是二进制代码,还是会由JIT编译执行,JIT有很多优化,另外大多数场景也只会JIT一次,加上省略了JS加载,语法分析各种的过程,才会有着比JS更好的性能 。
另外因为WASM是中间码的格式,所以理论上任何语言C#、RUST、Java、Go都可以将代码编译为WASM,然后放到浏览器中执行 。比如C#火热的Blazor项目,就是将C#编译为WASM,然后使C#代码能在浏览器中运行 。
另外聊一聊WASI(WebAssembly System Interface),我们知道WASM有着不错的可移植性和安全性(目前浏览器运行都是沙箱运行 , 对于权限管控很严格),那么就有一群大佬就说,我们是不是能脱离浏览器单独运行WASM程序呢?于是就产生了一个标准的系统接口 , 大家都按照这样的方式来生成WASM,调用系统API,然后我们开发一个Runtime,让大家的WASM程序都能在这上面运行 。
举个不严谨的例子说明一下WASI就是比如:

  • C# => MSIL => CLR(Mono、CoreCLR)
  • Java => 字节码 => JVM(HotSpot VM、ZingVM)而现在我们可以:
  • C# => WASM => WASI(wasmtime、wasmedge) 。
各位应该就明白了,WASI其实就是个运行时的规范,大家编译成WASM放上去就能跑 。
试试将.NET7编译为WASM并在Docker上运行

文章插图
所以现在对于它的观点就是,觉得它在Server后端领域目前来说不是一个很价值的东西,因为可移植性好的语言比比皆是,比如C#、Java、Go等等 。
拿性能来说 , 对于这样的中间语言性能无关就是JIT和GC,WASI的JIT和GC能做的像C#、Java这样的JIT、GC性能那么好吗?这个目前来说是存在疑问的,至少在短时间内很难追平其它平台十多年的优化 。
再说WASM的另一个优点 , 就是体积小和启动快,现在C#支持NativeAOT、Java有GraalVM、Go和Rust之类的本身就是编译型语言,启动速度和体积都很不错,WASM在这个方面其实不占优势 。
.NET编译为WASM好了,言归正传,我们来试试.NET7上面的WASM 。.NET7目前已经发布 , 我们需要使用最新的版本,如下图所示:
试试将.NET7编译为WASM并在Docker上运行

文章插图
然后我们创建一个简单的控制台项目,用于输出斐波那契数列和执行耗时,代码如下所示 (这并不性能最优的实现,只是这样子实现简单):
using System.Diagnostics;namespace PublishDotNetToWASM;public static class Program{public static void Main(){// warmulong sum = 0;foreach (var i in Fibonacci().Take(1000)){sum += i;}// runsum = 0;var sw = Stopwatch.StartNew();foreach (var i in Fibonacci().Take(100000)){sum += i;}sw.Stop();Console.WriteLine($"Result:{sum}, Timespan:{sw.ElapsedTicks} Ticks");}private static IEnumerable<ulong> Fibonacci(){ulong current = 1, next = 1;while (true){yield return current;next = current + (current = next);}}}接下来为了将.NET程序发布成WASM , 我们需要安装Wasi.Sdk预览包,这个预览包是Steve Sanderson大佬做的支持,可以将.NET程序编译为WASM,截止至目前版本信息如下所示:
<PackageReference Include="Wasi.Sdk" Version="0.1.2-preview.10061" />运行dotnet publish -c Release命令,将我们的应用程序发布为WASM格式,在发布过程中,需要下载MinGW作为编译器,网络环境不好的同学 , 需要使用proxy,稍微等待一会就顺利的发布成功了:
试试将.NET7编译为WASM并在Docker上运行

文章插图
运行WASM程序此时我们可以安装一下Wasmtime来执行我们的程序 , 通过https://wasmtime.dev/下载安装:
试试将.NET7编译为WASM并在Docker上运行

文章插图
然后就可以直接使用wasmtime命令运行我们的程序 , 我分别使用wasmtimedotnet运行了我们的程序:
试试将.NET7编译为WASM并在Docker上运行

文章插图
可见目前来说WASM的性能还是惨不忍睹的,等一等后续的优化吧 。
将.NET发布到Docker WASI再来看看我们的Docker,对于Docker支持WASI我感到并不意外,因为Docker的容器化对于直接执行的WASM来说还是比较重,支持它是一个拓宽影响力的好事 。具体的执行模型如下所示,对于WASM应用有着不同的执行方式 。不再使用

推荐阅读