【译】使用.NET将WebAssembly扩展到云(一)

 点击上方“DotNet NB”关注公众号

回复“1”获取开发者路线图

65e2b6ed9caef0cb0823d1abf13f83ad.gif

学习分享 丨作者 / 郑 子 铭    

这是DotNet NB 公众号的第226篇原创文章

原文 | Richard Lander

翻译 | 郑子铭

WebAssembly(Wasm)是一种令人兴奋的新虚拟机和(汇编)指令格式。Wasm 诞生于浏览器,是 Blazor 项目的重要组成部分。Wasm 的第二个行动是针对应用程序和功能的云计算。WebAssembly 系统接口 (WASI) 是新的推动者,为 WebAssembly 代码提供了一种安全地跨语言调用和实现任意 API 的方法。现在可以使用 .NET 8 中的 wasi 实验工作负载通过 .NET 创建 WASI 应用程序。我们正在探索这些新技术并在此环境中运行 .NET 应用程序……。真的,任何地方。

这篇文章将帮助您了解 Wasm 的广泛使用,并描述 .NET 已经可以实现的功能。他们说历史不会重演,但会押韵。我们又回来进行另一轮“一次编写,随处运行”。WASI 应用程序是可移植的二进制文件,可以在任何硬件或操作系统上运行,并且不特定于任何编程语言。这一次,感觉不一样了。这不仅仅是供应商的神经;一切都是中立的。

Wasm 和 WASI

Wasm 可能会为我们提供云计算的重启,并承诺提供单一云原生二进制文件、更高的密度和更便宜的多租户。出于同样的原因,它也开启了边缘计算的可能性。事实上,CloudFlare 和 Fastly 已经使用 Wasm 在边缘托管公共计算。

Wasm 与在 Linux 容器中运行应用程序不同,后者是对现有标准和代码的(良好且聪明的)重新打包。Wasm 更像是在没有操作系统的环境中运行应用程序,只有汇编代码、内存和对外部世界的标准化(和门控)访问(通过 WASI)。

Build 2023 上的 Hyperlight 演示(4m 视频)深入了解了支持 Wasm 的云的外观。它演示了在新的轻量级安全虚拟机管理程序中运行的 Blazor 应用程序。Hyperlight 激发了新托管范例的想象力。

WebAssembly 系统接口 (WASI)、WebAssembly 接口类型 (WIT) 和 WebAssembly 组件模型是最新一轮 Wasm 创新的关键规范。它们基本上仍处于设计阶段并正在经历重大变化。这篇文章(以及 .NET 8 实现)以 WASI Preview 1 为中心。我们希望 .NET 9 实现使用 WASI Preview 2。

WIT 和 wit-bindgen 使用任何源语言编写的组件都可以与主机系统进行通信。WIT 对 C# 支持的实现由 @silesmo 领导。Wasm 和 WIT 一起定义了应用程序二进制接口(ABI)。

我们期望 WASI 成为一组标准的 WIT 类型,提供对低级功能的访问(例如获取时间和读取文件)。这些低级类型有效地形成了跨编程语言和操作系统的“Wasm 标准库”。例如,我们从来没有 Rust 开发人员和 .NET 开发人员可以同时使用的标准和共享功能。历史上还没有任何广泛部署的本机代码公开具有 OO 形状(如接口)的 API,可以跨编程语言和操作系统使用。

标准 WIT 类型以 wasi- 开头,定义“平台”。您可以将它们视为与 .NET 中的系统命名空间类似的方式(与 WASI 中的“S”匹配)。继续类比,您可以在 System 命名空间之外创建自己的 .NET 命名空间,WIT 也是如此。

这些帖子在更详细地构建 WASI 方面做得非常出色。

  • 标准化 WASI:在 Web 之外运行 WebAssembly 的系统接口

  • 宣布字节码联盟:为 WebAssembly 构建一个默认安全、可组合的未来

  • WebAssembly:开发人员更新的路线图

即将到来的承诺是能够采用现有的 .NET 应用程序或库并将其编译为 Wasm 目标。我们的设计本能是在 .NET 堆栈中实现相对较高的 WIT 接口(例如为 wasi-sql 创建 ADO.NET 数据提供程序),这将使现有代码(包括许多现有的 NuGet 包)能够正常工作,特别是对于没有本机依赖项。

Wasm 应用程序在 Wasm 运行时中运行,例如 wasmtime。与 Docker 非常相似,您可以使用特定功能配置该运行时。例如,如果您希望 Wasm 代码能够访问键/值存储,您可以向其公开一个键/值接口,该接口可以由本地数据库或云服务支持。

Wasm 运行时旨在可嵌入到应用程序中。事实上,有一个 wasmtime 包用于在 .NET 应用程序中托管 Wasm。.NET 代码可以作为 Wasm 运行,但 .NET 应用程序可以托管 wasmtime?!?是的,这个空间开始看起来是圆形的。虽然这些场景看起来很循环,但它们最终可能非常有用,与 AppDomain 的使用方式大致相似。这也让人想起所有“docker in docker”场景。
我们期待更多的创新、更多的 Wasm 运行时和更多的行业参与者。事实上,Wasm 已经升级为 W3C 规范。W3C 是 Wasm 的完美家园,让它成长为广泛的行业规范,就像之前的 HTML 和 XML 一样。

wasi-实验工作量

.NET 8 包含一个名为 wasi-experimental 的新工作负载。它构建在 Blazor 使用的 Wasm 功能之上,将其扩展为在 wasmtime 中运行并调用 WASI 接口。它还远未完成,但已经实现了有用的功能。

让我们从理论转向演示新功能。

安装 .NET 8 SDK 后,您可以安装 wasi-experimental 工作负载。

dotnet workload install wasi-experimental

注意:此命令可能需要管理员权限,例如在 Linux 和 macOS 上使用 sudo。

您还需要安装 wasmtime 来运行您即将生成的 Wasm 代码。

使用 wasi-console 模板尝试一个简单的示例。

$ dotnet new wasiconsole -o wasiconsole
$ cd wasiconsole
$ cat Program.cs 
using System;

Console.WriteLine("Hello, WASI Console!");
$ dotnet run
WasmAppHost --runtime-config /Users/rich/wasiconsole/bin/Debug/net8.0/wasi-wasm/AppBundle/wasiconsole.runtimeconfig.json
Running: wasmtime run --dir . -- dotnet.wasm wasiconsole
Using working directory: /Users/rich/wasiconsole/bin/Debug/net8.0/wasi-wasm/AppBundle
Hello, WASI Console!

该应用程序使用 wasmtime 运行。这里没有 x64 或 Arm64,只有 Wasm。

dotnet run 提供额外的信息(在控制台输出中)来帮助解释发生了什么。未来这种情况可能会改变。与主机系统的所有交互均由 wasmtime 管理。

我们可以更深入地查看 AppBundle 目录。

$ ls -l bin/Release/net8.0/wasi-wasm/AppBundle
total 24872
-rwxr--r--  1 rich  staff  11191074 Oct 31 07:53 dotnet.wasm
-rwxr--r--  1 rich  staff   1526128 Oct 11 14:00 icudt.dat
drwxr-xr-x  6 rich  staff       192 Nov 19 19:35 managed
-rwxr-xr-x  1 rich  staff        48 Nov 19 19:35 run-wasmtime.sh
-rw-r--r--  1 rich  staff       915 Nov 19 19:35 runtimeconfig.bin
drwxr-xr-x  2 rich  staff        64 Nov 19 19:35 tmp
-rw-r--r--  1 rich  staff      1457 Nov 19 19:35 wasiconsole.runtimeconfig.json
$ ls -l bin/Release/net8.0/wasi-wasm/AppBundle/managed 
total 3432
-rw-r--r--  1 rich  staff    27136 Nov 19 19:35 System.Console.dll
-rw-r--r--  1 rich  staff  1711616 Nov 19 19:35 System.Private.CoreLib.dll
-rw-r--r--  1 rich  staff     5632 Nov 19 19:35 System.Runtime.dll
-rw-r--r--  1 rich  staff     5120 Nov 19 19:35 wasiconsole.dll

SDK 将应用程序发布到独立部署中。.NET 运行时 — dotnet.wasm — 已经编译为 Wasm(在我们的构建机器上)。应用程序和 dotnet.wasm 在 wasmtime 中一起加载,运行所有代码。应用程序的实际托管代码(位于托管目录中)在运行时解释,就像 Blazor WebAssembly 一样。 @yowl 和 @SingleAccretion 社区成员一直在尝试 Wasm 和原生 AOT。

您可能想知道为什么我们需要将所有这些文件分开,而显然更好的选择是拥有一个 wasiconsole.wasm 文件。我们也可以这样做,但稍后会在帖子中介绍它,因为我们需要在机器上安装更多的软件(目前 wasi 实验工作负载不包含这些软件)。

RuntimeInformation 告诉我们什么?

RuntimeInformation 是我最喜欢的类型之一。它让我们更好地了解目标环境。

我们可以稍微更改示例以显示一些更有用的信息。

using System;
using System.Runtime.InteropServices;

Console.WriteLine($"Hello {RuntimeInformation.OSDescription}:{RuntimeInformation.OSArchitecture}");
Console.WriteLine($"With love from {RuntimeInformation.FrameworkDescription}");

它产生这个输出。

Hello WASI:Wasm
With love from .NET 8.0.0

第一行很有趣。操作系统是WASI,架构是Wasm。这是有道理的,有更多的背景。文章前面提到 Wasm 可以被认为是“无操作系统”,但是我们不能简单地称之为 Wasm,因为现有的浏览器和 WASI 环境有很大不同。因此,该环境唯一一致的名称是 WASI,而 Wasm 明确是“芯片架构”。

Wasm 是一个 32 位计算环境,这意味着 2^32 字节是可寻址的。但是,Wasm 运行时可以配置为使用 memory64,从而可以访问 >4GB 的内存。我们还没有对此的支持。

访问主机文件系统

Wasmtime(和其他 Wasm 运行时)提供将主机目录映射到来宾目录的选项。从用户的角度来看,这与使用 Docker 进行卷挂载类似,但实现细节有所不同。

让我们看一个依赖目录安装的简单应用程序。它使用 Markdig 包将 markdown 转换为 HTML。公平地说,Markdig 并不是为了以 Wasm 的身份运行而编写的。只要能够为其创建一个舒适的管理环境,Markdig 就会很高兴,这就是我们所做的。

让我们在 Mac M1 (Arm64) 机器上尝试一下。

$ pwd
/Users/rich/git/wasm-samples/tomarkup
$ dotnet publish
$ cd bin/Release/net8.0/wasi-wasm/AppBundle 
$ cat run-wasmtime.sh
wasmtime run --dir . dotnet.wasm tomarkup $*
$ ./run-wasmtime.sh 
A valid inputfile must be provided.
$  wasmtime run --dir . --mapdir /markdown::/Users/rich/markdown --mapdir /tmp::/Users/rich dotnet.wasm tomarkup $* /markdown/README.md /tmp/README.html
$ ls ~/*.html
/Users/rich/README.html
$ cat ~/markdown/README.md | head -n 3  
# .NET Runtime

[![Build Status](https://dev.azure.com/dnceng-public/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dev.azure.com/dnceng-public/public/_build/latest?definitionId=129&branchName=main)
$ cat ~/README.html | head -n 3       
<h1>.NET Runtime</h1>
<p><a href="https://dev.azure.com/dnceng-public/public/_build/latest?definitionId=129&amp;branchName=main"><img src="https://dev.azure.com/dnceng-public/public/_apis/build/status/dotnet/runtime/runtime?branchName=main" alt="Build Status" /></a>
<a href="https://github.com/dotnet/runtime/labels/help%20wanted"><img src="https://img.shields.io/github/issues/dotnet/runtime/help%20wanted?style=flat-square&amp;color=%232EA043&amp;label=help%20wanted" alt="Help Wanted" /></a>

--mapdir 正在挂载从主机到来宾的目录。

如您所见,Markdown 文件已转换为 HTML。为了简洁起见,显示了每个文件的前三行。

目录挂载所需的 CLI 手势目前有点不方便。这是我们需要在未来版本中考虑的内容。这实际上是一个 dotnet run 和 wasmtime run 应该如何关联的问题。

但它能算字数吗?

我最近出版了《System.IO 的便利》,重点关注字数统计。我们能否获得与 Wasm 相同的代码来运行并看看它的运行速度有多快?

该文章中的字数统计基准测试在 Linux x64 上运行。让我们保持不变,但这次以 Wasm 身份运行。

$ pwd
/Users/rich/git/convenience/wordcount/count
$ grep asm count.csproj 
    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
    <WasmSingleFileBundle>true</WasmSingleFileBundle>
$ dotnet publish
$ cd bin/Release/net8.0/wasi-wasm/AppBundle/
$ WASMTIME_NEW_CLI=0 wasmtime run --mapdir /text::/home/rich/git/convenience/wordcount count.wasm $* /text/Clarissa_Harlowe
    11716  110023  610515 /text/Clarissa_Harlowe/clarissa_volume1.txt
    12124  110407  610557 /text/Clarissa_Harlowe/clarissa_volume2.txt
    11961  109622  606948 /text/Clarissa_Harlowe/clarissa_volume3.txt
    12168  111908  625888 /text/Clarissa_Harlowe/clarissa_volume4.txt
    12626  108593  614062 /text/Clarissa_Harlowe/clarissa_volume5.txt
    12434  107576  607619 /text/Clarissa_Harlowe/clarissa_volume6.txt
    12818  112713  628322 /text/Clarissa_Harlowe/clarissa_volume7.txt
    12331  109785  611792 /text/Clarissa_Harlowe/clarissa_volume8.txt
    11771  104934  598265 /text/Clarissa_Harlowe/clarissa_volume9.txt
        9     153    1044 /text/Clarissa_Harlowe/summary.md
   109958  985714  5515012 total

我更新了项目文件以包含 wasi-wasm 和 true 并注释掉 PublishAot 相关属性。我还添加了一个runtimeconfig.template.json 文件。未对应用程序代码进行任何更改。

现在,我们将整个应用程序放在一个文件包中。

$ ls -l bin/Release/net8.0/wasi-wasm/AppBundle/
total 6684
-rw-r--r-- 1 rich rich    1397 Nov 19 19:59 count.runtimeconfig.json
-rwxr-xr-x 1 rich rich 6827282 Nov 19 19:59 count.wasm
-rw-r--r-- 1 rich rich     915 Nov 19 19:59 runtimeconfig.bin
-rwxr-xr-x 1 rich rich      27 Nov 19 19:59 run-wasmtime.sh
drwxr-xr-x 2 rich rich    4096 Nov 19 19:59 tmp

看起来好多了。该应用程序只有不到 7MB。我必须安装 WASI-SDK 才能使用 WasmSingleFileBundle 属性并设置环境变量以使 dotnetpublish 能够找到所需的工具。

$ echo $WASI_SDK_PATH
/home/rich/wasi-sdk/wasi-sdk-20.0/

wasmtime 最近发生了重大变化。我选择使用 WASMTIME_NEW_CLI=0 来恢复运行示例的旧行为。

让我们回到性能。首先,作为 wasm 运行(通过解释器执行托管代码):

$ time WASMTIME_NEW_CLI=0 wasmtime run --mapdir /text::/home/rich/git/convenience/wordcount count.wasm $* /text/Clarissa_Harlowe
    11716  110023  610515 /text/Clarissa_Harlowe/clarissa_volume1.txt
    12124  110407  610557 /text/Clarissa_Harlowe/clarissa_volume2.txt
    11961  109622  606948 /text/Clarissa_Harlowe/clarissa_volume3.txt
    12168  111908  625888 /text/Clarissa_Harlowe/clarissa_volume4.txt
    12626  108593  614062 /text/Clarissa_Harlowe/clarissa_volume5.txt
    12434  107576  607619 /text/Clarissa_Harlowe/clarissa_volume6.txt
    12818  112713  628322 /text/Clarissa_Harlowe/clarissa_volume7.txt
    12331  109785  611792 /text/Clarissa_Harlowe/clarissa_volume8.txt
    11771  104934  598265 /text/Clarissa_Harlowe/clarissa_volume9.txt
        9     153    1044 /text/Clarissa_Harlowe/summary.md
   109958  985714  5515012 total
Elapsed time (ms): 821
Elapsed time (us): 821223.8

real    0m0.897s
user    0m0.846s
sys 0m0.030s

现在有了我们对 Wasm 的(甚至更多)实验性原生 AOT 支持。

$ time WASMTIME_NEW_CLI=0 wasmtime run --mapdir /text::/home/rich/git/convenience/wordcount count.wasm $* /text/Clarissa_Harlowe
    11716  110023  610515 /text/Clarissa_Harlowe/clarissa_volume1.txt
    12124  110407  610557 /text/Clarissa_Harlowe/clarissa_volume2.txt
    11961  109622  606948 /text/Clarissa_Harlowe/clarissa_volume3.txt
    12168  111908  625888 /text/Clarissa_Harlowe/clarissa_volume4.txt
    12626  108593  614062 /text/Clarissa_Harlowe/clarissa_volume5.txt
    12434  107576  607619 /text/Clarissa_Harlowe/clarissa_volume6.txt
    12818  112713  628322 /text/Clarissa_Harlowe/clarissa_volume7.txt
    12331  109785  611792 /text/Clarissa_Harlowe/clarissa_volume8.txt
    11771  104934  598265 /text/Clarissa_Harlowe/clarissa_volume9.txt
        9     153    1044 /text/Clarissa_Harlowe/summary.md
   109958  985714  5515012 total
Elapsed time (ms): 60
Elapsed time (us): 60322.2

real    0m0.107s
user    0m0.064s
sys 0m0.045s

现在,在 Linux x64 上使用 CoreCLR 运行:

$ time ./app/count ../Clarissa_Harlowe/
    11716  110023  610515 ../Clarissa_Harlowe/clarissa_volume1.txt
    12124  110407  610557 ../Clarissa_Harlowe/clarissa_volume2.txt
    11961  109622  606948 ../Clarissa_Harlowe/clarissa_volume3.txt
    12168  111908  625888 ../Clarissa_Harlowe/clarissa_volume4.txt
    12626  108593  614062 ../Clarissa_Harlowe/clarissa_volume5.txt
    12434  107576  607619 ../Clarissa_Harlowe/clarissa_volume6.txt
    12818  112713  628322 ../Clarissa_Harlowe/clarissa_volume7.txt
    12331  109785  611792 ../Clarissa_Harlowe/clarissa_volume8.txt
    11771  104934  598265 ../Clarissa_Harlowe/clarissa_volume9.txt
        9     153    1044 ../Clarissa_Harlowe/summary.md
   109958  985714  5515012 total
Elapsed time (ms): 77
Elapsed time (us): 77252.9

real    0m0.128s
user    0m0.096s
sys 0m0.014s

这些都是有趣的结果。我们有解释、AOT 和 JIT 代码生成方法可供比较。Wasm 解释器能够在不到一秒的时间内计算(略低于)一百万个单词,而 AOT 编译的 Wasm 和 JIT 运行时可以在大约 100 毫秒内完成同样的操作。

49d5c530c68f57d5a2c123676c63e236.png

注意:Main 方法是运行 main 的时间,由 StopWatch 测量。流程是整个流程的持续时间,以时间来衡量。

此图表显示了上下文中的所有结果,包括 System.IO 的便利性帖子中的结果。

wasmtime JIT 将 Wasm 代码编译到目标环境(在本例中为 Linux+x64)。例如,可以使用 wamr 对 Wasm 代码进行 AOT。我将把它留到另一篇文章中。

原文链接

Extending WebAssembly to the Cloud with .NET

推荐阅读:

【译】.NET 8 网络改进(三)

【译】.NET 8 网络改进(二)

【译】.NET 8 网络改进(一)

【译】发布 .NET Aspire 预览版 2(二)

【译】发布 .NET Aspire 预览版 2(一)

【译】宣布推出适用于 .NET 的 Azure Migrate 应用程序和代码评估工具

bb3cd5fa1da23b2365a67c9b309f49c8.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值