WebAssembly

简介

WebAssembly是由Mozilla、谷歌、微软和苹果共同开发的一种面向Web的二进制格式。该格式名为WebAssembly,可以作为任何编程语言的编译目标,使应用程序可以运行在浏览器或其它代理中。

在WebAssembly之前,这四家公司已经分别自己开发了类似的技术来扩展浏览器的能力,比如微软的typescript、苹果的FLTJIT、谷歌的PNaCI以及Molliza的asm.js。最后这四家公司联起手来搞了个WebAssembly。现在主流的浏览器已经开始尝试支持WebAssembly。 Emscripten编译流程

C/C++  =>  LLVM  =>  Emscripten   =>  asm.js

在编程成LLVM IR的时候编译器会对代码做很多优化,因而能性能上也会有所提升。

可以做什么

通过WebAssembly我们可以把一些C/C++现有的工具或库编译成JS通过浏览器或者Node去执行。比如编译:

  • unreal、unity等游戏引擎
  • FFmpeg等编解码/库
  • SqlLite数据库
  • Python、Lua等运行环境
  • …… 更多场景请访问 Use Case

环境搭建

要使用WebAssembly我们先要安装Emscripten和Binaryen这两套工具,通过Emscripten我们可以把Emscripten编译成asm.js格式的JavaScript,然后通过Binaryen生成最终的WebAssembly二进制文件。

emscripten安装

安装主要有两种方式,一种是通过emsdk来安装,还有一种则是直接通过源码安装。通过emsdk安装只需要通过emsdk install,emsdk activate等几行简单命令安装就会自动完成。如果想理解其中细节最好还是从源码安装,下面以Ubuntu14.04(64位)为例通过源码进行安装。

//首先安装相关依赖
sudo apt-get update
sudo apt-get install python2.7 nodejs build-essential cmake git-core default-jre
//编译Fastcomp,Fastcomp是Emscripten的默认编译器,可以将Clang生成的LLLVM IR编译到JavaScript。
mkdir myfastcomp && cd myfastcomp
git clone https://github.com/kripken/emscripten-fastcomp
cd emscripten-fastcomp
git clone https://github.com/kripken/emscripten-fastcomp-clang tools/clang
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;JSBackend" -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DCLANG_INCLUDE_EXAMPLES=OFF -DCLANG_INCLUDE_TESTS=OFF
../configure --enable-optimized --disable-assertions --enable-targets=host,js
make -j2 //-j2根据实际情况分配cpu核数,执行后等待编译即可。
//Fastcomp编译完成后,建立一个目录从git上把emscripten代码拉下来
git clone https://github.com/kripken/emscripten.git
//进入emscripten的目录执行./emcc --help会在当前用户的目录下生成一个.emscripten的文件,里面包含emcc运行的相关配置,这个把LLVM_ROOT的路径指向我们前面编译的Fastcomp的路径,其他配置如果没有特殊需求保持默认就好,LLVM配置如下:
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/home/vagrant/myfastcomp/emscripten-fastcomp/build/bin') #
//到此安装完成

安装完成后可以通过emcc -v查看相关依赖的配置信息

然后通过编一个最简单的hello来测试下。

#include<stdio.h>
int main(){
        printf("hello\n");
        return 0;
}
vagrant@vagrant-ubuntu-trusty-64:~/wamscode$ ../emscripten/emscripten/emcc hello.c
vagrant@vagrant-ubuntu-trusty-64:~/wamscode$ ls
a.out.js  hello.c
vagrant@vagrant-ubuntu-trusty-64:~/wamscode$ nodejs a.out.js
hello

对于一些大型的开源项目,通常里面提供了自动化编译的脚本。比如通常我们要编译一个开源的C/C++程序我们会执行下面类似的命令。

./configure
make

如果是要编译到WebAssembly的话则需要用emcc去替换掉原来的gcc等编译器,在Emscripten里面已经为我们提供相关脚本方便我们操作。

./emconfigure ./configure
./emmake make
./emcc [-Ox] project.bc -o project.js
Emscripten执行过程

如图所示,emcc使用Clang将C/C++编译成LLVM bitcode,然后通过Fastcomp将bitcode编程成JavaScript,生成的JavaScripit可以在浏览器或者Node环境下执行。

Emscripten运行环境

由于C/C++的执行环境和浏览器不同,编译的时候Emscripten不能只是做下代码的转换,还需要把C/C++的环境也实现。比如:本地运行的C/C++程序可以通过libc或libxx的API去读取本地文件,但是由于浏览器的限制,JavaScript不能再浏览器中读取本地文件,所以Emscripten提供了一个虚拟的文件系统来实现文件读写的需求。

Binaryen

通过Emscripten可以将C/C++的代码编译成Javascript,但还不是最终的WebAssembly二进制文件。通过binaryen我们可以获得最终的二进制文件(.wasm)。

安装
git clone https://github.com/WebAssembly/binaryen.git
cd binaryen
cmake .& make

编译工具

  • asm2wasm : 将asm格式的JavaScript编译成S-表达式的.wast文件
  • wasm-as : 把S-表达式的wast表达式文件编译成最终的WebAssembly二进制文件

在浏览器中运行wasm二进制文件

利用上面的工具我们可以把代码编译成.wasm的二进制文件,接下来用一个简单的例子介绍下WebAssembly二进制文件的编译生成以及在浏览器中的运行。

//my.asm.js
function MyFirstModule(global) {
    "use asm";
    var exp = global.Math.exp;
    function doubleExp(value) {
        value = +value;
        return +(+exp(+value) * 2.0);
    }
    function secondFunc(){
        return +(2);
    }
    return { doubleExp: doubleExp ,secondFunc : secondFunc};
}
//把JavaScript编译成S-表达式
asm2wasm my.asm.js -o my.asm.wast
//my.asm.wast文件内容
(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)
  (export "secondFunc" $secondFunc)
  (func $doubleExp (param $0 f64) (result f64)
    (f64.mul
      (call_import $exp
        (get_local $0)
      )
      (f64.const 2)
    )
  )
  (func $secondFunc (result f64)
    (f64.const 2)
  )
)
把S-表达式编译成二进制的wasm文件
wasm-as my.asm.wast -o my.asm.wasm

生成的wasm二进制文件如下,其中前8个字节成为Preamble,是对整个二进制文件的描述。前四个字字节用来判断是否为有效wasm模块,后面四个字节为wasm的版本。Preamble还有Type section、Function section、Memory section、Export section等,具体的编码细节可参照Binary Encoding

//my.asm.wasm
0061 736d 0b00 0000 0474 7970 658a 8080
8000 0240 0104 0104 4000 0104 0669 6d70
6f72 7492 8080 8000 0100 0b67 6c6f 6261
6c2e 4d61 7468 0365 7870 0866 756e 6374
696f 6e83 8080 8000 0200 0106 6d65 6d6f
7279 8580 8080 0080 0280 0201 0665 7870
6f72 7498 8080 8000 0200 0964 6f75 626c
6545 7870 010a 7365 636f 6e64 4675 6e63
0463 6f64 65a5 8080 8000 0290 8080 8000
0014 0018 0100 1200 0000 0000 0000 408b
8a80 8080 0000 1200 0000 0000 0000 4004
6e61 6d65 9880 8080 0002 0964 6f75 626c
6545 7870 000a 7365 636f 6e64 4675 6e63
00

二进制文件现在得到了,接下我们拿到浏览器里去运行。由于WebAssembly支持的浏览器还少,首先我们需要获得一个支持WebAssembly的浏览器

  • Chrome最新版或者Chrome Canary中启用chrome://flags/#enable-webassembly
  • Microsoft Edge预览版
  • Firefox Nightly中打开about:config设置javascript.options.wasm为true

  • var myFirstModule; fetch("my.asm.wasm") .then(function(response) { return response.arrayBuffer(); }) .then(function(buffer) { var dependencies = { "global": {}, "env": {} }; dependencies["global.Math"] = window.Math; var moduleBufferView = new Uint8Array(buffer); myFirstModule = Wasm.instantiateModule(buffer,dependencies); });

浏览器支持

asm.js:

wasm二进制:

  • Chrome最新版或者Chrome Canary中启用chrome://flags/#enable-webassembly
  • Microsoft Edge预览版
  • Firefox Nightly中打开about:config设置javascript.options.wasm为true

项目示例

videoconvert.js

通过Emscripten把FFmpeg编译成JavaScript,从而实现在浏览器里进行转码。 https://bgrins.github.io/videoconverter.js/

sqllite.js

将C++版本的SqlLite编译成JavaScript,在浏览器中实现数据的功能。https://github.com/kripken/sql.js/

ocrad.js

JavaScript实现图片上的文字库识别。 https://github.com/antimatter15/ocrad.js/

pypy.js

将Python的运行环境编译成JavaScript。http://pypyjs.org/

jslinux

在浏览器器运行linux。 http://bellard.org/jslinux/

Angry Bots

官网上展示一个3D游戏。 https://webassembly.github.io/demo/

原文发布于微信公众号 - QQ音乐技术团队(gh_287053a877e6)

原文发表时间:2016-08-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jerry的SAP技术分享

如何实现Windows宿主系统和虚拟机ubuntu系统文件互相访问

我的宿主操作系统是Windows 10,使用Oracle的Virtual Box安装了Ubuntu。

13030
来自专栏京东技术

开发属于自己的插件 | IDEA &amp; Android Studio插件开发指南

谷轩宇——从事安卓开发,目前效力于通天塔技术开放组是否曾经被ide重复繁琐的操作所困扰,又或者没有心仪的UI控件而难受。那么请阅读这篇文章,掌握idea插件的开...

1.4K20
来自专栏云霄雨霁

死锁、饥饿和活锁

36640
来自专栏java一日一条

RESTful API 最佳实践

在参考了GitHub API设计和大量博客文章后总结了一下RESTful API的设计,分享如下。想要更好的理解RESTful API首先需要理解如下概念:

68320
来自专栏逸鹏说道

【已解决】WinPhone模拟器报错:模拟器没法确定来宾虚拟机通信的主机ID地址。某些功能已被禁用

【已解决】WinPhone模拟器报错:模拟器没法确定来宾虚拟机通信的主机ID地址。某些功能已被禁用 先看警告 ? 再看错误信息 ? ? 计算机管理打不开就==>...

27530
来自专栏JavaEdge

操作系统之内存管理内存管理3.1 内存管理的概念3.2 内存覆盖与内存交换3.3 内存连续分配管理方式3.4 内存非连续分配管理方式

71060
来自专栏JavaEdge

操作系统之文件管理一、文件与文件系统二、文件控制块和文件目录三、文件的物理结构四、文件系统的实现五、文件系统实例(UNIX)六、UNIX文件系统一、文件系统实例(FAT)二、文件操作的实现三、文件系统

81460
来自专栏静晴轩

浅谈android中的目录结构

之前在android游戏开发中就遇到本地数据存储的问题:一般情形之下就将动态数据写入SD中存储,在没有SD卡的手机上就需另作处理了;再有在开发android应用...

360100
来自专栏北京马哥教育

深入浅出:Linux设备驱动之中断与定时器

“我叮咛你的 你说 不会遗忘 你告诉我的 我也全部珍藏 对于我们来说 记忆是飘不落的日子 永远不会发黄 相聚的时候 总是很短 期待的时候 总是很长 岁月的溪水边...

53190
来自专栏Hadoop实操

如何在Redhat中配置R环境

R是一套完整的数据处理、计算和制图软件系统。其功能包括:数据存储和处理系统;数组运算工具(其向量、矩阵运算方面功能尤其强大);完整连贯的统计分析工具;优秀的统计...

94150

扫码关注云+社区

领取腾讯云代金券