内容简介:作者:Michael Yuan原文链接:如何在 Deno TypeScript 应用程序访问 Rust 函数?
作者:Michael Yuan
原文链接: https://www.infoq.com/article...
如何在 Deno TypeScript 应用程序访问 Rust 函数?
要点:
- Deno 和 Node.js 都在基于 C/C ++ 的运行时上执行 JavaScript 以实现高性能。
- Deno 是单个二进制应用程序,与 NPM 模块不兼容,并且没有简单的方法能将本机模块合并到应用程序中。
- WebAssembly 提供了一种在 Deno 应用程序中运行高性能代码的方法。
- WebAssembly 用于服务端应用程序,是安全、轻便且轻量级的容器。
- Rust 编译器 工具 链为 WebAssembly 提供了强大的支持。
备受期待的 Deno 项目不久前发布了 1.0 版本。 Deno 由 Node.js 的创建者之一 Ryan Dahl 发起,解决 Ryan 所认为的“我为 Node.js 感到遗憾的十件事”。
Deno 没有采用 NPM 和臭名昭著的 node_modules
。 Deno 是一个单一的二进制可执行文件,运行用 TypeScript 和 JavaScript 编写的应用程序。
但是,尽管 TypeScript 和 JavaScript 适用于大多数的 Web 应用程序,但它们不能满足计算密集型任务,例如神经网络训练和推理、机器学习和密码学。 实际上,Node.js 经常需要使用本地库来执行这些任务(例如,使用 openssl 进行加密)。
如果没有类似 NPM 的系统来合并本机模块,我们如何在 Deno 上编写需要本机性能的服务端应用程序呢? WebAssembly 将提供帮助! 在本文中,我们用 Rust 编写高性能函数,将 Rust 函数编译为 WebAssembly,然后在 Deno 应用程序中运行它们。
TL;DR
在 GitHub 上 clone 或者 fork Deno starter 模板 。按照下面的说明进行操作,只需5分钟,就可以在 Deno 中运行 WebAssembly 函数(由 Rust 编写)。
背景知识
Node.js 之所以非常成功,是因为它为开发人员带来了两全其美的优势:JavaScript 的易用性(尤其是基于事件的异步应用程序)以及 C/C ++ 的高性能。 Node.js 应用程序是用 JavaScript 编写的,但是在基于 C / C ++ 的本机运行时中执行,例如,Google V8 JavaScript 引擎和许多本机库模块。 Deno 希望复制此公式以取得成功,但不同的是,Deno 用 TypeScript 和 Rust 支持现代技术堆栈。
Deno 是用 Rust 编写的,基于 V8 的 JavaScript 和 TypeScript 的简单、现代且安全的运行时。 -deno.land网站。
在 《我对 Node.js 感到遗憾的十件事 》这个著名演讲中,Node.js 的创建者 Ryan Dahl 解释了为什么要开始 Deno 并将 Deno 看做 Node.js 的竞争对手甚至替代者。Dahl 的遗憾集中在 Node.js 如何管理第三方代码和模块上。
- 用于将 C 模块连接到 Node.js 的复杂构建系统。
-
package.json
,node_modules
,index.js
以及其他 NPM 工件非常复杂,但是这并不是必须的。
因此,Deno 在管理依赖项时做出了一些非常有意识和自觉的选择。
import
这很好。但是,需要更高性能的应用程序应该怎么做呢?特别是需要在几秒钟之内执行复杂的神经网络模型的AI即服务应用程序,该如何处理呢? 在 Deno 和 Node.js 中,许多函数都是通过 TypeScript 或 JavaScript API 调用的,但是这些函数都是在用 Rust 或 C 语言编写的本机代码执行。在 Node.js 中,始终可以选择从 JavaScript API 调用第三方的本地库。 但是我们目前无法在 Deno 中执行此操作吗?
Deno 中的 WebAssembly 支持
WebAssembly 是一种轻量级虚拟机,旨在以接近本机的速度执行可移植的字节码。你可以将 Rust 或 C C ++ 函数编译为WebAssembly 字节码,然后从 TypeScript 访问这些函数。对于某些任务,这种方式比执行 TypeScript 编写的等效函数要快得多。例如,IBM 发布的研究发现在某些数据处理算法中使用 Rust 和 WebAssembly ,可以将 Node.js 的执行速度提高 1200% 至 1500% 。
Deno 内部使用 Google V8 引擎。 V8 不仅是 JavaScript 运行时,还是 WebAssembly 虚拟机。 Deno 开箱即用地支持WebAssembly。 Deno 为 TypeScript 应用程序提供了一个API,以调用 WebAssembly 中的函数。
实际上,WebAssembly 中已经实现了一些流行的 Deno 组件。例如,使用 Emscripten 将 sqlite 的 C 源代码编译到 WebAssembly 中来创建 Deno 中的 sqlite 模块 。 Deno WASI 组件 使 WebAssembly 应用程序可以访问底层操作系统资源,例如文件系统。本文将展示如何在 Rust 和 WebAssembly 中编写高性能 Deno 应用程序。
设置
第一步当然是安装 Deno , 在大多数操作系统中,只需要一行命令
$ curl -fsSL https://deno.land/x/install/install.sh | sh
既然我们要用 Rust 写函数,也需要安装 Rust 语言的编译器与工具.
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
最后, ssvmup 工具自动执行构建过程并生成所有工件,使 Deno 应用程序可以轻松调用 Rust 函数。同样,需要安装 ssvmup 依赖项。
$ curl https://raw.githubusercontent.com/second-state/ssvmup/master/installer/init.sh -sSf | sh `
注意:ssvmup 使用 wasm-bindgen
在 JavaScript 和 Rust 源代码之间自动生成“胶水”代码,以便 JavaScript 和 Rust 可以使用各自的本机数据类型进行通信。没有 ssvmup,函数参数和返回值将限于 WebAssembly 本地支持的简单类型(即32位整数)。例如,如果没有 ssvmup 和 wasm-bindgen
,则无法使用字符串或数组。
Hello world
首先,让我们看一下Deno 的 hello world 示例,从 GitHub 获取 hello world 源代码和应用程序模板。
Rust 函数位于 src/ lib.rs 文件中,只需在输入字符串前加上“ hello” 即可。注意, say()
函数使用# [wasm_bindgen]
进行了注释,从而使 ssvmup 可以生成必要的“管道”。基于此,我们可以从 TypeScript 调用 Rust 函数。
#[wasm_bindgen] pub fn say(s: &str) -> String { let r = String::from("hello "); return r + s; }
Deno 应用程序位于 deno / server.ts 文件中。该应用程序从 pkg / functions_lib.js 文件导入 Rust 的 say()
函数,该文件由 ssvmup 工具生成。 functions_lib.js
文件名取决于 Cargo.toml 文件中定义的 Rust 项目名称。
import { serve } from "https://deno.land/std@0.54.0/http/server.ts"; import { say } from '../pkg/functions_lib.js'; type Resp = { body: string; } const s = serve({ port: 8000 }); console.log("http://localhost:8000/"); for await (const req of s) { let r = {} as Resp; r.body = say (" World\n"); req.respond(r); }
现在,运行 ssvmup 将 Rust 函数构建为 Deno WebAssembly 函数。
$ ssvmup build --target deno
ssvmup 成功完成后,您可以检查 pkg / functions_lib.js
文件,了解如何使用 Deno WebAssembly API 执行已编译的 WebAssembly 文件 pkg / functions_lib.wasm
。
接下来,运行 Deno 应用程序。 Deno 需要读取文件系统的权限,因为它需要加载 WebAssembly 文件。同时,Deno 也需要访问网络,因为它需要接收和响应 HTTP 请求。
$ deno run --allow-read --allow-net deno/server.ts
现在,在另一个终端窗口中,可以访问 Deno Web 应用程序,通过 HTTP 连接 say hello!
$ curl http://localhost:8000/
hello World
一个复杂的例子
这个入门模板项目包括许多详细的示例,展示了如何在 Deno TypeScript 和 Rust 函数之间传递复杂的数据。这是 src/ lib.rs 中其他的 Rust 函数。请注意,它们都用 #[wasm_bindgen]
注释。
#[wasm_bindgen] pub fn obfusticate(s: String) -> String { (&s).chars().map(|c| { match c { 'A' ..= 'M' | 'a' ..= 'm' => ((c as u8) + 13) as char, 'N' ..= 'Z' | 'n' ..= 'z' => ((c as u8) - 13) as char, _ => c } }).collect() } #[wasm_bindgen] pub fn lowest_common_denominator(a: i32, b: i32) -> i32 { let r = lcm(a, b); return r; } #[wasm_bindgen] pub fn sha3_digest(v: Vec<u8>) -> Vec<u8> { return Sha3_256::digest(&v).as_slice().to_vec(); } #[wasm_bindgen] pub fn keccak_digest(s: &[u8]) -> Vec<u8> { return Keccak256::digest(s).as_slice().to_vec(); }
也许最有趣的是 create_line()
函数。这个函数需要两个 JSON 字符串。每个字符串代表一个 Point
结构,并返回一个代表 Line
结构的 JSON 字符串。注意, Point
和 Line
结构都使用 Serialize
和 Deserialize
进行了注释,以便 Rust
编译器自动生成必要的代码来支持它们与 JSON
字符串之间的转换。
use wasm_bindgen::prelude::*; use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] struct Point { x: f32, y: f32 } #[derive(Serialize, Deserialize, Debug)] struct Line { points: Vec<Point>, valid: bool, length: f32, desc: String } #[wasm_bindgen] pub fn create_line (p1: &str, p2: &str, desc: &str) -> String { let point1: Point = serde_json::from_str(p1).unwrap(); let point2: Point = serde_json::from_str(p2).unwrap(); let length = ((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)).sqrt(); let valid = if length == 0.0 { false } else { true }; let line = Line { points: vec![point1, point2], valid: valid, length: length, desc: desc.to_string() }; return serde_json::to_string(&line).unwrap(); } #[wasm_bindgen] pub fn say(s: &str) -> String { let r = String::from("hello "); return r + s; }
接下来,让我们检查一下 JavaScript 程序 deno/test.ts ,这显示了如何调用 Rust 函数。 String
和 &str
是 JavaScript 中的简单字符串, i32
是数字,而 Vec <u8>
或 &[8]
是 JavaScript Uint8Array
。 JavaScript 对象需要先通过 JSON.stringify()
或 JSON.parse()
才能传入 Rust 函数或从 Rust 函数返回。
import { say, obfusticate, lowest_common_denominator, sha3_digest, keccak_digest, create_line } from '../pkg/functions_lib.js'; const encoder = new TextEncoder(); console.log( say("SSVM") ); console.log( obfusticate("A quick brown fox jumps over the lazy dog") ); console.log( lowest_common_denominator(123, 2) ); console.log( sha3_digest(encoder.encode("This is an important message")) ); console.log( keccak_digest(encoder.encode("This is an important message")) ); var p1 = {x:1.5, y:3.8}; var p2 = {x:2.5, y:5.8}; var line = JSON.parse(create_line(JSON.stringify(p1), JSON.stringify(p2), "A thin red line")); console.log( line );
运行 ssvmup 构建 Rust 库之后,在 Deno 运行时中运行 deno/test.ts 会产生以下输出:
$ ssvmup build ``-``-``target deno ``...`` Building the wasm file and JS shim file ``in`` pkg``/`` ``...`` $ deno run ``-``-``allow``-``read deno``/``test``.``ts hello SSVM N dhvpx oebja sbk whzcf bire gur ynml qbt ``246 ``Uint8Array``(``32``)`` ``[ ``87``,`` ``27``,`` ``231``,`` ``209``,`` ``189``,`` ``105``,`` ``251``,`` ``49``, ``...`` ``... ``] ``Uint8Array``(``32``)`` ``[ ``126``,`` ``194``,`` ``241``,`` ``200``,`` ``151``,`` ``116``,`` ``227``, ``...`` ``... ``] ``{ `` points``:`` ``[`` ``{`` x``:`` ``1.5``,`` y``:`` ``3.8`` ``}``,`` ``{`` x``:`` ``2.5``,`` y``:`` ``5.8`` ``}`` ``]``,`` valid``:`` ``true``,`` length``:`` ``2.2360682``,`` desc``:`` ``"A thin red line" ``}`
接下来是什么?
现在,我们可以创建 Rust 函数,并从 Deno TypeScript 应用程序访问 Rust 函数。接下来,我们可以用 Rust 函数编写计算密集型任务,并通过 Deno 提供高性能和安全的 Web 服务。此类服务的示例包括 机器学习 和 图像识别 。
以上所述就是小编给大家介绍的《如何在 Deno 应用程序中调用 Rust 函数》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- iOS应用程序如何调用以太坊智能合约
- Android里应用程序,应用程序窗口和视图对象之间的关系
- 使用 Bluemix、Watson Discovery 和 Cloudant 构建移动应用程序来分析其他应用程序
- ChromeOS 终端应用程序暗示其即将支持 Linux 应用
- 这几天在C程序中有哪些应用程序?
- Zookeeper详解-应用程序(七)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。