从Rust到远方:ASM.js星系

2024-06-23 01:08
文章标签 rust js 远方 asm 星系

本文主要是介绍从Rust到远方:ASM.js星系,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源: https://mnt.io/2018/08/28/from-rust-to-beyond-the-asm-js-galaxy/

这篇博客文章是这一系列解释如何将Rust发射到地球以外的许多星系的文章的一部分:

  • 前奏,

  • WebAssembly 星系

  • ASM.js星系(当前这一集)

  • C 星系

  • PHP星系,以及

  • NodeJS 星系

Rust解析器将要探索的第二个星系是ASM.js。这篇文章会解释什么是ASM.js,怎样编译博客解析器到ASM.js以及如何在浏览器中和Javascript一起使用ASM.js. 使用ASM.js的目标是当作WebAssembly不可用的备用方案。我强烈建议你读读前一篇关于WebAssembly的文章,因为他们有很多共同的地方

#什么是ASM.js,为什么需要ASM.js

Web应用的主要语言是Javascript,任何想要运行在Web上的应用都必须编译成Javascript,比如游戏。但是这里有个问题:编译输出文件非常重(即使是WebAssembly),这样Javascript虚拟机将很难对这样的代码做优化,这就导致运行缓慢或者执行低效(可以考虑用游戏的规模来做例子)。而且在Javascript为编译目标的情况下,一些语言基础设施变得毫无用处,比如eval

如果有一种新的语言可以作为编译目标,而且也能被Javascript虚拟机运行会怎么样?这就是WebAssembly,但是在2013年的时候解决方案就是ASM.js:

asm.js, 是一个严格的Javascript子集,它能够被用作编译器输出的底层的,高效的目标语言。这个子语言高效的描述>了一个沙盒虚拟机,可以适用于内存不安全的语言,像C或者C++。静态和动态的组合校验让Javascript可以对有效 的asm.js代码使用一种叫做ahead-of-time(AOT)的编译时优化策略。

因此ASM.js程序其实只是一个普通的Javascript程序。它不是一个新的语言而是Javascript的一个子集。它可以被任何Javascript虚拟机执行。但是,有个特殊的魔法声明use asm;,会指示虚拟机用ASM.js引擎来优化这个程序。

ASM.js通过算术运算引入了类型作为标记系统。比如,x|0表示x是一个整数,+x表示x是一个双精度小数,fround(x)表示x是一个浮点数。下面的例子声明了一个函数fn increment(x: u32) -> u32:

function increment(x) {x = x | 0;return (x + 1) | 0;
}

另一个重要的区别是ASM.js以模块的方式来运行,以和Javascript隔离。这个模块是一个需要3个参数的函数:

  • stdlib,一个带有引用到标准库API的对象

  • foreign,一个带有用户定义功能的对象(比如通过WebSocket发送一些东西)

  • heap,一个表示内存的数组(因为内存是手动管理的)

但是它仍然是Javascript。因此一个好消息是如果你的虚拟机没有对ASM.js做优化,那么它运行起来就是普通的Javascript程序,如果有优化,那么你就可以得到不错的性能提升。

640?wx_fmt=png

这个图片展示了3个基准测试,分别对于不同的Javascript引擎:Firefox, Firefox+asm.js, Google, 和Native。

记住ASM.js是被设计为一种编译目标。因此一般你不需要特别关心,因为那是编译器的活。对把C或者C++编译到Web的一个典型的编译和执行流程如下

640?wx_fmt=png

Emscripten,如上图所示,是一个在这个Web平台演进过程中非常重要的一个项目。

它是一个用来编译输出asm.js和WebAssembly的工具链,基于LLVM之上,能够让C和C++程序以接近原生应用的速 度运行在Web上,而且不需要任何插件。

如果你和ASM.js或者WebAssembly打交道,迟早有一天你会看到这个名字。I will not explain deeply what ASM.js is with a lot of examples. I recommend instead to read Asm.js: The Javascript Compile Target by John Resig, or Big Web app? Compile it! by Alon Zakai. 我不会用大量的例子来深入的解释ASM.js.我推荐两本书:Asm.js: The Javascript Compile Target by John Resig, 或者 Big Web app? Compile it! by Alon Zakai.

我们的过程有点不一样。我们不会直接编译Rust代码到ASM.js,而是先编译为WebAssembly,然后再编译为ASM.js。

#Rust ? ASM.js

640?wx_fmt=png

这个篇章会非常的短,应该说是最简单的一篇。要编译Rust到ASM.js你需要先编译到WebAssembly(参考前一篇文章)然后再编译WebAssembly二进制到ASM.js。

事实上真正需要ASM.js的地方是那些不支持WebAssembly的浏览器,比如IE。它只是我们在Web运行我们程序的一个后备方案。

下面看看这个流程:

  • 编译你的Rust项目到WebAssembly

  • 编译你的WebAssembly二进制为ASM.js模块

  • 优化和精简ASM.js模块

wasm2js会是你最好的朋友,它用来编译你的WebAssembly二进制为ASM.js模块,它属于Binaryen项目。下面假设我们已经有了程序的WebAssembly二进制,只需要运行下面的命令:

$ wasm2js --pedantic --output gutenberg_post_parser.asm.js gutenberg_post_parser.wasm

在这一步,gutenberg_post_parser.asm.js 有212kb。这个文件包含ECMAScript 6代码。注意这里因为考虑了老浏览器如IE,所以代码需要一点小小的转换来优化和精简ASM.js模块,我们用uglify-es工具,如下:

$ #  转换代码, 嵌入入到一个函数.
$ sed -i '' '1s/^/function GUTENBERG_POST_PARSER_ASM_MODULE() {/; s/export //' gutenberg_post_parser.asm.js
$ echo 'return { root, alloc, dealloc, memory }; }' >> gutenberg_post_parser.asm.js$ # 精简代码.
$ uglifyjs --compress --mangle --output .temp.asm.js gutenberg_post_parser.asm.js
$ mv .temp.asm.js gutenberg_post_parser.asm.js

就像我们优化WebAssembly二进制一样,我们也可以gzip和brotli压缩输出文件:

$ # Compress.
$ gzip --best --stdout gutenberg_post_parser.asm.js > gutenberg_post_parser.asm.js.gz
$ brotli --best --stdout --lgwin=24 gutenberg_post_parser.asm.js > gutenberg_post_parser.asm.js.br

最终我们得到了下面的文件尺寸:

  • .asm.js: 54kb,

  • .asm.js.gz: 13kb,

  • .asm.js.br: 11kb.

都是非常小的!

思考一下,这里面涉及到了很多的转换:从Rust到WebAssembly到Javascript/ASM.js。。。工具的数量相对于工作量是非常少的。这展现了一个良好设计的流水线和不同工作组的人的良好合作。

题外话:如果你在读这篇博客,我假设你是一个开发者。既然如此,我很确定你可以花几个小时阅读源代码就像欣赏大师的画作。但是你有没有想过把Rust编译出来的Javascript输出是什么样的?

640?wx_fmt=png

我可以说非常的喜欢它!

#ASM.js ? Javascript

输出的gutenberg_post_parser.asm.js文件包含一个唯一的函数叫做:GUTENBERG_POST_PARSER_ASM_MODULE,它返回一个对象包含了4个私有函数。

  1. root,语法根

  2. alloc,来分配内存

  3. dealloc,释放内存

  4. memory,内存缓冲区

如果你看过上一篇WebAssembly的文章那么你会对这些概念感到熟悉。不要指望root会返回一个完整的AST,它只会返回一个内存指针,数据需要进一步编解码,也需要用同样的方式对内存进行读写。是的,相同的方式。因此边界层代码完全是一样的。你是否还记得在WebAssembly中作为Javascript边界的Module对象?那和GUTENBERG_POST_PARSER_ASM_MODULE函数返回的完全是一样的。你甚至可以用这个对象替换。

所有的代码都在这里。它完全重用WebAssembly的Javascript边界层代码,只是Module有一些不一样,也没有加载WebAssembly二进制。结果是ASM.js边界代码只用了34行就写出来了,压缩后仅有218个字节。

#结论

我们已经看到ASM.js可以在只支持Javascript的环境中(像IE)作为WebAssembly的备用方案,并可适配环境打开或者关闭ASM.js优化。

输出的ASM.js文件以及其边界层代码都非常的小。设计上,ASM.js边界层代码重用WebAssembly的Javascript边界层代码,因此同样只有一小部分外部接口代码需要审查和维护,这非常的有用。

我们已经在前一篇文章中看到Rust很快的速度。我们也已经在比较WebAssembly版本和纯Javascript版本解析器中看到同样速度结论。然而这个结论是否也适用于ASM.js模块呢?其实在这种情况下ASM.js只是一个备用方案,和其他备用方案一样通常都比较明显的慢于目标实现。下面是一个将Rust解析器作为ASM.js模块运行的基准测试:

testJavascript parser (ms)Rust parser as an ASM.js module (ms)speedup
demo-post.html15.3682.718× 6
shortcode-shortcomings.html31.0228.004× 4
redesigning-chrome-desktop.html106.41619.223× 6
web-at-maximum-fps.html82.9227.197× 3
early-adopting-the-future.html119.88038.321× 3
pygmalian-raw-html.html349.07523.656× 15
moby-dick-parsed.html2,543.75361.423× 7

ASM.js模块版本的Rust解析器比纯Javascript实现平均快6倍。中位数也是6倍。这和WebAssembly版本比较还是有较大差距,但是考虑到这是个备用方案,而且平均已经快了6倍了,已经很不错了!

因此不仅是整个工作流因为Rust而变得更加安全,而且得到的结果也比Javascript快。

在这个系列的后续文章中我们将会看到Rust会到达很多的星系,Rust越多的往后旅行,也会变得更加有趣。

谢谢阅读!

这篇关于从Rust到远方:ASM.js星系的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085905

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

【Rust练习】12.枚举

练习题来自:https://practice-zh.course.rs/compound-types/enum.html 1 // 修复错误enum Number {Zero,One,Two,}enum Number1 {Zero = 0,One,Two,}// C语言风格的枚举定义enum Number2 {Zero = 0.0,One = 1.0,Two = 2.0,}fn m

使用JS/Jquery获得父窗口的几个方法(笔记)

<pre name="code" class="javascript">取父窗口的元素方法:$(selector, window.parent.document);那么你取父窗口的父窗口的元素就可以用:$(selector, window.parent.parent.document);如题: $(selector, window.top.document);//获得顶级窗口里面的元素 $(

js异步提交form表单的解决方案

1.定义异步提交表单的方法 (通用方法) /*** 异步提交form表单* @param options {form:form表单元素,success:执行成功后处理函数}* <span style="color:#ff0000;"><strong>@注意 后台接收参数要解码否则中文会导致乱码 如:URLDecoder.decode(param,"UTF-8")</strong></span>

js react 笔记 2

起因, 目的: 记录一些 js, react, css 1. 生成一个随机的 uuid // 需要先安装 crypto 模块const { randomUUID } = require('crypto');const uuid = randomUUID();console.log(uuid); // 输出类似 '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(