试试将.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 放上去就能跑。4c1cf7338653aa9ee59ca665e523c3ec.png

所以现在对于它的观点就是,觉得它在 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 目前已经发布,我们需要使用最新的版本,如下图所示:2190ea379c0d36f01dbee48be1443e64.png

然后我们创建一个简单的控制台项目,用于输出斐波那契数列和执行耗时,代码如下所示 (这并不性能最优的实现,只是这样子实现简单)

using System.Diagnostics;

namespace PublishDotNetToWASM;

public static class Program
{
    public static void Main()
    {
        // warm
        ulong sum = 0;
        foreach (var i in Fibonacci().Take(1000))
        {
            sum += i;
        }

        // run
        sum = 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作为编译器,网络环境不好的同学,需要想办法科学上网,稍微等待一会就顺利的发布成功了:fd81cccb040ace848011cc6576ba9525.png

运行 WASM 程序

此时我们可以安装一下Wasmtime来执行我们的程序,通过https://wasmtime.dev/下载安装:8883aeed40093e56ee4cb1f4a49b728c.png

然后就可以直接使用wasmtime命令运行我们的程序,我分别使用wasmtimedotnet运行了我们的程序:855e799ed2e12b126c0eccea403136c6.png

可见目前来说 WASM 的性能还是惨不忍睹的,等一等后续的优化吧。

将.NET 发布到 Docker WASI

再来看看我们的 Docker,对于 Docker 支持 WASI 我感到并不意外,因为 Docker 的容器化对于直接执行的 WASM 来说还是比较重,支持它是一个拓宽影响力的好事。具体的执行模型如下所示,对于 WASM 应用有着不同的执行方式。不再使用runc而是wasmedge

47a562791ea8c3b113f7b5c7109b5943.pngwasmedge也是一个实现了 WASI 标准的 WASM 运行时,和上文提到的 wasmtime 一样。

要实现在 Docker 上运行 WASM 程序需要安装 Docker 的预览版,链接https://docs.docker.com/desktop/wasm/1ed60c86c8ca6b00312aede1f98e2292.png

然后我们整一个 Dockerfile,我们直接依赖 scratch 镜像即可,因为它不需要其它的基础镜像(暂时我没有使用.NET7 的多段构建镜像,听大佬说目前貌似有问题)。

FROM scratch
COPY ./bin/Release/net7.0/PublishDotNetToWASM.wasm /PublishDotNetToWASM.wasm
ENTRYPOINT [ "PublishDotNetToWASM.wasm" ]

再使用下面的命令构建 Docker 镜像,由于是 wasm 镜像,所以需要带额外的参数。

docker buildx build --platform wasi/wasm32 -t publishdotnettowasm .

可以看到打包出来的镜像是非常小的,只有 3.68MB。234f34970a77bf668d6462b6e5fe697f.png

运行的话也很简单,用下方的命令即可,需要指定 runtime 为io.containerd.wasmedge.v1,另外也需要指定 paltform。

docker run --rm --name=publishdotnettowasm --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm32 publishdotnettowasm

我把 dotnet 原生运行、wasmtime 运行、docker WASI 运行都跑了一下,可以发现目前来说是惨不忍睹。43e8d4559cb036f2e9b3cb2cef7d0182.png

总结

以上就是如何将.NET7 程序发布到 WASM,然后在 Docker 最新的 WASI 中运行的样例,目前来看基本的运行都已经 OK,不过正如我前面提到的,现在性能还是太受影响了。

这不仅仅是在.NET 平台上,其它语言 Rust、C、C++编译为 WASM 上都有明显的性能下降。603b51bccfd8a9cec9834cf37bfd75f0.png

思来想去可能在一些插件化和不需要性能很好的场景 WASI 会比较用。不过这些都需要时间慢慢见证,毕竟存在即合理,像 JS 这样的语言不一样好好的?

我们可以拭目以待,看看 WASM/WASI 会不会给我们带来其它惊喜,期待后续 Steve Sanderson 大佬和 WASM 社区的相关优化。

源码链接

https://github.com/InCerryGit/PublishDotNetToWASM

参考文献

https://www.docker.com/blog/docker-wasm-technical-preview/

https://www.zhihu.com/question/304577684/answer/1961085507

https://arghya.xyz/articles/webassembly-wasm-wasi/

https://laurentkempe.com/2022/10/31/experimenting-with-dotnet-7-wasm-and-wasi-on-docker/

技术群:添加小编微信并备注进群
小编微信:mm1552923   公众号:dotNet编程大全
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值