【译】WebAssembly 初尝

本文转载自:众成翻译
译者:文蔺
连接:http://www.zcfy.cc/article/1031
原文:http://cultureofdevelopment.com/blog/build-your-first-thing-with-web-assemblyjavascript

拖拖拉拉很久,终于把我的博客整出来了。鸣谢 @pinggod。厚着脸安利一下,地址是 http://www.wemlion.com/。欢迎访问,欢迎收藏。html

头一次据说 WebAssembly 的时候就以为很酷,而后就超兴奋地开始尝试。但从一开始尝试的过程就不顺利,愈来愈让人灰心。本文的目的就是解决问题,让你免受困扰。java

beware of cliff

读者须知

本文写做于 2016 年 6 月 24 日。WebAssembly 是一项很新的、不稳定的技术;随着其标准化过程发展,本文中的任何内容均可能是错误的。git

不过先无论了....github

WebAssembly 是什么

好吧,官网是这么描述的:web

WebAssembly,或者称做 wasm,是一项适用于 Web 编译的可移植的、体积与加载高效的格式。(WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.)chrome

嗯...什么鬼?什么格式?文本(Text)?二进制(Binary)?老实说,这个描述真糟糕。因此无论它,收起那些 binggo 游戏卡(buzzword bingo cards,一种填词游戏,这些词一般都是流行语,阅读https://en.wikipedia.org/wiki/Buzzword_bingo了解更多 —— 译者注),根据我全部的经验来描述吧:npm

WebAssembly/wasm 是用来编写高性能的、浏览器无关的 Web 组件的一种字节码规范。(WebAssembly or wasm is a bytecode specification for writing performant, browser agnostic web components.)数组

有此妙语,听起来超棒,但仍然没 get 到点,接下来重点来了。WebAssembly 经过静态类型变量实现性能提高,运行时静态类型变量引用比动态类型变量更有效率。WebAssembly 由 W3C Community Group 制定,最终将被全部规范兼容的浏览器支持。还有杀手锏,最终咱们可使用任何语言编写这些 Web 组件(web components)。浏览器

听起来酷了不少,不是么?

一块儿开始吧

学习新东西的时候,我一般会找尽量最简单的例子来看它是如何工做的。不幸的是,对 WebAssembly 来讲,这不太现实。在当前阶段,wasm 仅仅只是字节码规范。想象回到 1996 年,假如太阳公司(Sun Microsystems)的一些工程师们带来了 JVM,但却...没有 Java....若果然如此,我想当时的对话多是这样的:

—— “伙计们,快来看看咱们作的这个执行字节码的虚拟机!”
—— “真棒!但咱们给它怎么写代码?”

HelloWorld.class

图:字节码形式的 HelloWorld

—— “嗯..这问题提得好。等会儿我查查看。”
—— “真棒,若是遇到了任何问题,告诉咱们你的想法,在咱们的 github page 上贴出来。”
—— “你说对啦。咱们如今先去看看其余项目。”

这个例子有些糟糕,由于 JVM 是基于 Java 语言的;尽管如此,但愿你仍是 get 到点了。若是都没有将代码编译为字节码的工具,要起步就很困难了。那咱们要怎么开始?

WebAssembly 以前有什么

多数技术都是创新的结果,特别是当合理的尝试成为正式规范时。wasm 也不例外,它其实是 asm.js 的工做的延续, asm.js 是一个编写 javascript 组件的的规范,可编译为静态类型。wasm 的规范拓展了这些创意,它接受任何语言编译而成的字节码,这些字节码做为二进制文件而非文本文件经过网络传输;规范由不少来自主流浏览器厂商的表明们一块儿制定,而非仅仅是 Mozilla。

asm.js 仅仅是一个使用 javascript 语言特征的最小子集编写 javascript 的规范。你能够手写一些简单的 asm.js 代码,若是你想弄脏你的手,这正是极好的方式。(等会儿最好将这放在单独的文件中,一般约定文件名格式为 your-module-name.asm.js。))

function MyMathModule(global) {
    "use asm";
    var exp = global.Math.exp;
    function doubleExp(value) {
        value = +value;
        return +(+exp(+value) * 2.0);
    }
    return { doubleExp: doubleExp };
}

这还不是一个特别有用的函数,但符合规范。若是你以为这很二,别人也是这么以为的,不过基本上每个字符都是必须的。在这当中,一元运算符 + 的做用是类型注解,这样编译器会知道那些变量是 double 类型的,运行时就没必要再次分辨它们是什么。它至关挑剔,若是你把什么地方弄得一团糟,火狐控制台会给你一些合理的错误信息。

若是你想在浏览器中使用,像下面这样:

var myMath = new MyMathModule(window);
for(var i = 0; i < 5; i++) {
    console.log(myMath.doubleExp(i));
}

一切正常的话,结果大概像下图这样:

asm.js success

开始尝试 WebAssembly

如今咱们已经有了一个能够工做的 asm.js 代码片断,可使用 WebAssembly github page 提供的工具将其编译为 wasm。本身克隆代码仓库构建工具吧。这最麻烦了。这些工具一直在不断发展,代码会时不时挂掉,特别是在 Windows 环境下。

无论你是用 Windows 仍是 Mac,电脑上必需要安装 make 和 cmake 命令行工具。若是你在使用 Windows,你还须要安装 Visual Studio 2015。Mac 用户按照这里的说明 操做;Windows 用户按照这个说明操做。

building binaryen

图: Windows 下的工具构建

对 WebAssembly 团队来讲,发布能够工做的二进制文件意味着朝着正确的方向前进了一大步。

构建成功以后,binaryen 目录下会有一个 bin 文件夹,其中有一些用来将 asm.js 转换为 wasm 的工具。第一个工具是 asm2wasm.exe。它将 asm.js 代码转换为 .s 格式的代码,这些代码是生成 wasm 所需的抽象语法树(AST)的文本表现形式。运行工具,最终会获得相似下面的东西:

(module
  (memory 256 256)
  (export "memory" memory)
  (type $FUNCSIG$dd (func (param f64) (result f64)))
  (import $exp "global.Math" "exp" (param f64) (result f64))
  (export "doubleExp" $doubleExp)
  (func $doubleExp (param $0 f64) (result f64)
    (f64.mul
      (call_import $exp
        (get_local $0)
      )
      (f64.const 2)
    )
  )
)

之后咱们能够逐行分析上面的代码,但如今我只想让你看下它的样子。我还得提醒你一点,由于 wasm 是二进制格式的,因此像你今天对 javascript 所作的那样右击、查看源码是行不通的。从头至尾都是二进制码。目前的计划是查看 wasm 模块源码时对二进制格式进行反汇编,让人能读懂。

接下来要作的是使用 wasm-as.exe.s 格式的代码转换为 wasm 二进制码。运行文件,最后就能获得浏览器须要的真正的 wasm 二进制码。

building wasm from asm.js

图:将 asm.js 转换为 wasm 二进制码

wasm bytecode

图:wasm 二进制码

紧接着,安装最新版的 FirefoxChrome Canary,并启用 WebAssembly。

若是你使用的是 Firefox,在地址栏中输入 about:config,点击“确认我会保证当心”。而后在搜索框中输入 wasm,双击 javascript.options.wasm 将值设置为 true,而后重启浏览器。

若是你使用的是 Chrome Canary,打开 chrome://flags,往下翻,找到 Experimental WebAssembly,点击“启用”连接,再重启浏览器。

最后一步就是让模块在浏览器中跑起来。初次尝试时,这又是一大痛点,彻底不知道怎么作。在规范中使用 wasm 模块的 API 一点都没找到。最后我在 Canary 的控制台上输入 WebAsse,并无任何提示。接着输入 Was 的时候,提示出来了!控制台上打印出的对象大概最简陋的文档了,不过这时候我忽然想到,能够用一些其余工具 (emscripten) 将代码编译为 wasm。不过这是另一篇博客的话题了。

一段时间以后,鼠标落在了 WebAssembly 的设计文档仓库上。我看到了一个名为 JS.md 的文件,点击以后,有一个 API 的说明。仔细看顶部斜体的文字。但最精彩的部分仍是最底部的代码片断,演示了如何最低限度地加载模块。我所须要作的就是拆出相关部分进行尝试。

fetch("my-math-module.wasm")
    .then(function(response) {
        return response.arrayBuffer();
    })
    .then(function(buffer) {
        var dependencies = {
            "global": {},
            "env": {}
        };
        dependencies["global.Math"] = window.Math;
        var moduleBufferView = new Uint8Array(buffer);
        var myMathModule = Wasm.instantiateModule(moduleBufferView, dependencies);
        console.log(myMathModule.exports.doubleExp);
        for(var i = 0; i < 5; i++) {
            console.log(myMathModule.exports.doubleExp(i));
        }
    });

把代码放到 html 文件中,启动本地文件服务器,在浏览器中加载页面。下面是在浏览器中的结果:

wasm in a browser

浏览器中运行的 wasm (至少尝试运行了)

我估计须要去提交一个 bug 报告了。记着,一切都是实验性的、不稳定的,因此当此类事情发生的时候,别灰心丧气。

keep calm and file bug reports

恭喜你

你已经完成了第一个 WebAssembly 组件。接下来作些什么?目前咱们碰到的还只是皮毛而已。在本例中手写 asm.js 很重要,但须要时间和耐心。使用 emscripten 将应用转换为 asm.js 要简单多了。关于这一点,我强烈建议你阅读 asm.js 规范,特别是内存模型的部分,由于其中的许多概念都被迁移到 WebAssembly 上了。另一个怪异的事情是,目前还不能直接将数组做为函数参数。人们已经达成共识,这须要改变,但规范中还没有有关于这一点的。去看看指针逻辑吧。

还有一点,在 wasm 中作一些工做的时候,你可能发展实际上 WebAssembly 还没普通的 javascript 运行得快。记住,现代的 javascript 引擎已是高度优化的,wasm 要遇上这速度还须要时间。WebAssembly 还还没有进入准备生产的阶段。

若是你有任何关于 wasm 或者本文中提到的工具的问题,在 Stack Overflow 中提出来,记得标上恰当的 tag。

相关文章
相关标签/搜索