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("hellon");
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/
- ASP.NET Core的配置(5):配置的同步[ 实例篇]
- TensorFlow 深度学习笔记 卷积神经网络
- 利用EntLib授权机制实现对ASP.NET页面的自动授权
- ASP.NET Core的配置(5):配置的同步[设计篇]
- 详解Redis内部运作机制
- TensorFlow深度学习笔记 循环神经网络实践
- 从客户端Web应用程序访问Bluemix服务
- 云改变传统银行业面貌的5种方式
- ASP.NET MVC中的ActionFilter是如何执行的?
- C语言嵌入式系统编程修炼之屏幕操作
- max-width:100%在td或者table-cell里渲染不符合预期小笔记
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
- 使用Ansible自动化您的(云或者本地)机器
- 像Apache Storm一样简单的分布式图计算
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- rxjs里的Observable对象subscribe方法的执行原理
- Java正则表达式
- kubernetes 二进制安装部署手册
- 接口
- Redis的各种数据类型实践--String字符串
- 必应API接口node.js版 - 极客玩家大白
- FFmpeg--简介
- 推荐系统中的常用算法——DeepWalk算法
- TypeError: cannot unpack non-iterable NoneType object
- 原型模式
- Spring 整合 Mybatis
- 数据库PostrageSQL-关闭服务器
- 快速配置Azure DevOps代理服务器
- 数据库PostrageSQL-管理内核资源
- airtest操作夜神模拟器adb冲突解决办法