您的位置:  首页 > 技术 > 前端 > 正文

用 WasmEdge 在 WebAssembly 中运行 JavaScript

2021-09-16 15:00 https://my.oschina.net/u/4532842/blog/5263396 WasmEdge 次阅读 条评论

WebAssembly 最初是作为“浏览器中的 JavaScript 替代品”。其想法是在浏览器中安全地运行编译自 C/C++ 或 Rust 等语言的高性能应用程序。在浏览器中,WebAssembly 和 JavaScript 并列运行。

图 1. 浏览器中的 WebAssembly 和 JavaScript

随着云中越来越多地使用 WebAssembly ,Wasm 现在是云原生应用程序的通用 runtime。与类似 Docker 的应用程序容器相比,WebAssembly runtime 以更低的资源消耗实现更高的性能。云中 WebAssembly 的常见用例包括以下内容

可是,在云原生应用场景中,开发者常常想要使用 JavaScript 来编写商业应用。这意味着现在我们必须在 WebAssembly 内支持JavaScript。此外,我们应该支持在 WebAssembly runtime 中从 JavaScript 调用 C/C++ 或 Rust 函数,充分利用 WebAssembly 的计算效率。 WasmEdge WebAssembly runtime 已经能够做到这一点。

图 2. 云中的 WebAssembly 和 JavaScript

WasmEdge

WasmEdge 是一个领先的云原生 WebAssembly runtime,由 CNCF(云原生计算基金会) 托管 。它是当今市场上最快的 WebAssembly runtime。 WasmEdge 支持所有标准的 WebAssembly 扩展以及 Tensorflow 推理、networking、KV 存储和图像处理等专有扩展。其编译器工具链不仅支持 WebAssembly 语言,如 C/C++、Rust、Swift、Kotlin 和 AssemblyScript,还支持常规 JavaScript

WasmEdge 应用可以嵌入到 C 程序、 Go 程序、 Rust 程序、 JavaScript 程序,或者操作系统的 CLI 中。WasmEdge 可以由以下工具管理:

现在,你可以在由 WasmEdge 支持的 serverless 函数、微服务和 AIoT 应用程序中运行 JavaScript 程序!WasmEdge 不仅可以运行普通的 JavaScript 程序,而且还允许开发者使用 Rust 和 C/C++ 在 WebAssembly 的安全沙箱中创建新的 JavaScript API。

在 WasmEdge 中构建 JavaScript 引擎

首先,让我们为 WasmEdge 构建一个基于 WebAssembly 的 JavaScript 解释器程序。这个程序基于 QuickJS ,并且带有 WasmEdge 扩展,例如 network socketsTensorflow 推理,并且作为 JavaScript API 被合并到解释器中。 首先,需要安装 Rust 来构建解释器。

如果你只想使用解释器来运行 JavaScript 项目,你可以跳过这个部分。确保你已经安装了 Rust和* *WasmEdge

Fork 或 clone wasmedge-quickjs Github repos 来开始。本文中所有的示例都会在 wasmedge-quickjs 里算。

$ git clone https://github.com/second-state/wasmedge-quickjs

按照 repo 中的说明,你就能够为 WasmEdge 构建 JavaScript 解释器。

$ `rustup target add wasm32-wasi`
$ cargo build --target wasm32-wasi --release

基于 WebAssembly 的 JavaScript 解释器程序位于被构建的 target 目录中。你现在可以尝试一个简单的 “hello world” JavaScript 程序 (example_js/hello.js),它会打印出命令行参数到控制器。

`args = args.slice(1)`
`print("Hello",…args)`

在 WasmEdge 的 QuickJS runtime 运行 hello.js 文件,如下。注意,命令行中的 --dir .:. 是要准许 wasmedge 读取文件系统中 hello.js文件的本地目录。

$ wasmedge --dir .:. target/wasm32-wasi/release/quickjs-rs-wasi.wasm example_js/hello.js WasmEdge Runtime
Hello WasmEdge Runtime

接下来,让我们尝试一些更高级的 JavaScript 程序。

JavaScript networking 示例

解释器支持 WasmEdge networking socket 扩展,以便 JavaScript 程序可以建立到互联网的 HTTP 连接。这是一个 JavaScript 示例。

let r = GET("http://18.235.124.214/get?a=123",{"a":"b","c":[1,2,3]})
print(r.status)
    
let headers = r.headers
print(JSON.stringify(headers))

let body = r.body;
let body_str = new Uint8Array(body)
print(String.fromCharCode.apply(null,body_str))

在 WasmEdge runtime 运行 JavaScript ,在 CLI 运行下面的命令。

$ wasmedge --dir .:. target/wasm32-wasi/release/quickjs-rs-wasi.wasm example_js/http_demo.js

你应该能在控制器中看到打印出的 HTTP GET 结果。

JavaScript Tensorflow 推理示例

解释器支持 WasmEdge Tensorflow lite 推理扩展,以便 JavaScript 可以运行 ImageNet 模型进行图像分类。 这是 JavaScript 的示例。

import {TensorflowLiteSession} from 'tensorflow_lite'
import {Image} from 'image'

let img = new Image('./example_js/tensorflow_lite_demo/food.jpg')
let img_rgb = img.to_rgb().resize(192,192)
let rgb_pix = img_rgb.pixels()

let session = new TensorflowLiteSession('./example_js/tensorflow_lite_demo/lite-model_aiy_vision_classifier_food_V1_1.tflite')
session.add_input('input',rgb_pix)
session.run()
let output = session.get_output('MobilenetV1/Predictions/Softmax');
let output_view = new Uint8Array(output)
let max = 0;
let max_idx = 0;
for (var i in output_view){
    let v = output_view[i]
    if(v>max){
        max = v;
        max_idx = i;
    }
}
print(max,max_idx)

在 WasmEdge runtime 运行 JavaScript ,你可以在 CLI 上执行以下操作,使用 Tensorflow 重新构建 QuickJS 引擎,然后使用 Tensorflow API 运行 JavaScript 程序。

$ cargo build --target wasm32-wasi --release --features=tensorflow
... ...
$ wasmedge-tensorflow-lite --dir .:. target/wasm32-wasi/release/quickjs-rs-wasi.wasm example_js/tensorflow_lite_demo/main.js
label:
Hot dog
confidence:
0.8941176470588236

注意:

  • --features=tensorflow 编译器标志使用 WasmEdge Tensorflow 扩展构建一个 QuickJS 引擎版本。
  • wasmedge-tensorflow-lite 程序是 WasmEdge 包的一部分。它是内置 Tensorflow 扩展的 WasmEdge runtime

现在应该可以看到 TensorFlow lite ImageNet 模型识别的食品名称。

--features=tensorflow编译器标志使用 WasmEdge Tensorflow 扩展构建 QuickJS 引擎的一个版本。 wasmedge-tensorflow-lite 程序是 WasmEdge 软件包的一部分。 它是内置 Tensorflow 扩展的 WasmEdge 运行时。

进一步加速

上面的 Tensorflow 推理示例需要 1-2 秒才能运行。这在 Web 应用场景中是可以接受的,但还能改进。回想一下,由于其 AOT(ahead of time)优化,WasmEdge 是当今最快的 WebAssembly Runtime。 WasmEdge 提供了一个 wasmedgec 实用程序来将 wasm文件编译为原生的 so 共享库。你可以使用 wasmedge 来运行 so 文件而不是 wasm 文件以获得更快的性能。

下面的例子使用了 wasmedgewasmedgec 的扩展版本来支持 WasmEdge Tensorflow 扩展。

$ wasmedgec-tensorflow target/wasm32-wasi/release/quickjs-rs-wasi.wasm quickjs-rs-wasi.so
$ wasmedge-tensorflow-lite --dir .:. quickjs-rs-wasi.so example_js/tensorflow_lite_demo/main.js
label:
Hot dog
confidence:
0.8941176470588236

这次的图像分类任务可以在0.1秒内完成。这至少是 10 倍的改进!

so 共享库不能跨机器和操作系统移植。你应该在部署和运行应用程序的机器上运行 wasmedecwasmedec-tensorflow

关于 QuickJS 的注释

选择 QuickJS 作为我们的 JavaScript 引擎可能会引发性能问题。由于缺乏 JIT 支持,QuickJS 是不是比 v8 慢很多?没错!但是……

首先,QuickJS 比 v8 小很多。事实上,它只需要 v8 消耗的 runtime 资源的 1/40(或 2.5%)。你可以在单个物理机上运行比 v8 函数多得多的 QuickJS 函数。

其次,对于大多数业务逻辑应用程序,原始性能并不重要。应用程序可能具有计算密集型任务,例如动态 AI 推理。 WasmEdge 允许 QuickJS 应用程序使用高性能 WebAssembly 来完成这些任务,而在 v8 中添加此类扩展模块并不容易。

第三,众所周知,许多 JavaScript 安全问题源于 JIT。也许在云原生环境中关闭 JIT 并不是一个坏主意!

接下来呢?

这些示例演示了如何在 WasmEdge 中使用 quickjs-rs-wasi.wasm JavaScript 引擎。 除了使用 CLI,你还可以使用 Docker / Kubernetes 工具来启动 WebAssembly 应用程序或将应用程序嵌入到你自己的应用程序或框架中,正如我们在本文前面所讨论的那样。

在接下来的两篇文章中,我将专注讨论如何将 JavaScript 与 Rust 结合使用,充分利用好这两种语言。

  • 如何在 WasmEdge 中将简单的 JavaScript 片段合并到高性能 Rust 应用中。
  • 如何在 WasmEdge 中将高性能本机函数用作 JavaScript API。

云原生 WebAssembly 中的 JavaScript 仍然是下一代云和边缘计算基础设施中的新兴领域。我们才刚刚开始!如果你有兴趣,欢迎加入我们的 WasmEdge项目。你也可以通过提出 feature request issue 告诉我们你的需求)。

  • 0
    感动
  • 0
    路过
  • 0
    高兴
  • 0
    难过
  • 0
    搞笑
  • 0
    无聊
  • 0
    愤怒
  • 0
    同情
热度排行
友情链接