WebAssembly编译之(1)-asm.js及WebAssembly原理介绍

WebAssembly介绍及产生历程

1、什么是WebAssembly、为什么WASM?

我们知道Web的应用几乎涵盖了大半个互联网应用;越多越多的Web应用层出不穷,而然Web最致命的劣势就是其在浏览其的运行效率特忙,尤其是web游戏的体验不佳。

而WebAssembly(即WASM)可以解决这个问题,WASM是一种可运行在浏览器的格式,它可移植、体积小、加载快并且兼容 Web 的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率;

2、WASM的产生背景

2012年,Mozilla的工程师(Alon Zakai)在研究LLVM时突然想到,如果能将c/c++代码编译成javascript代码,那许c/c++开发的代码都能在浏览器运行了。为此,他专门做了一个编译器项目,这个编译器即Emscripten,而这个编译器编译出来的js即asm.js

asm.js又是啥呢?

asm.js本质就是就Javascript代码,是它的子集,只是asm.js是严格的Javascript代码,在asm.js许多动态变量是不允许使用的。我们知道JS 是动态类型语言,而C是静态类型语言,所以,c/c++转成asm.js需要解决的前提是,采用静态声明;此外,JS是自动内存回收机制的;也就是说asm.js必须同时解决两个关键问题:

  • 静态类型语言
  • 手动内存回收

这就asm.js与普通JS最大区别,我们可以看一下asm.js什么样的,最明显的就是变量的声明定义

var a = 1;

var x = a | 0;  // x 是32位整数
var y = +a;  // y 是64位浮点数

sm.js 就要求事先声明类型,并且不得改变,这样就节省了类型判断的时间。asm.js 的类型声明有固定写法,变量 | 0表示整数,+变量表示浮点数。

看起来一时间好像并没啥不一样啊?我们写正常的JS,在写一段asm.js

// Javascript常规写法
var first = 5;
var second = first;

// asm.js要求的严格静态声明写法
var first = 5;
var second = first | 0;
// 除了参数x和y需要声明类型,函数的返回值也需要声明类型
function add(x, y) {
    
    
  x = x | 0;
  y = y | 0;
  return (x + y) | 0;
}
asm.js与WebAssembly有什么不同?

两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly(wasm) 是二进制字节码,因此运行速度更快、体积更小.

从长远来看,WebAssembly 的前景貌似更光明,因为本身asm.js 尽管是js,但是基本与WebAssembly一样已缺失了可读性,相比WebAssembly文件更小,执行效率更高,所以从这点看WebAssembly更有利。

Javascript 引擎的基本工作原理

Parser(语法词法分析)->AST(语法树)->Interpreter(生成ByteCode)->Profiler(分析可优化代码)->Compiler(生成优化后的Machine Code)

通常ByteCode已经是可执行了的,Profiler是单独的一条并行分析线程同步进行,发现有可优化的,则交个Compiler优化生成Machine Code后,再替换ByteCode;

我们看一下v8引擎的基本工作原理图:
在这里插入图片描述

Ignition:充当Interpreter(解释器), 同时收集 优化编译所需的信息交给TurboFan
TurboFan:充当Compiler(编译器), TurboFan利用Ignition 所收集的类型信息,将 Bytecode 转换为优化的 Machine Code

由于asm是严格模式,基本可跳过语法分析这一步,直接开始编译成汇编语言。这就是asm.js快的原因

据悉asm.js的效率可达源码的50% ,而WebAssembly可达70%

asm.js的升级——WebAssembly

但是,asm.js也有它的优势,尽管缺失可读性,但是,毕竟是文本,还是可读的;最关键的是asm.js是纯JS,所有浏览器都支持不会有兼容性问题

asm.js得到了Mozilla, Google,Microsoft,Applel 的支持,于是再进一步升级,于是就有了WebAssembly

asm.js说到底还是就是让JIT(即时编译)快一点,但是还是需要有ParserCompiler两个过程。而WebAssembly更激进,既然都具备AOT(预编译)了,直接就给你编译成可执行的二进制了,使之成为可直接与Machine Code打交道的汇编语言;

语言在运行之前通常都需要编译,JIT(Just-in-Time,即时编译) 和 AOT(Ahead-of-Time,预编译) 则是最常见的两种编译模式

目前大部分浏览器都支持WASM
在这里插入图片描述

3. Emscripten的编译执行流程

前面我们介绍到Emscripten是个可以把c/c++转成asm.js的编译器(当然也可以转为wasm),而Emscripten本身是一个基于LLVM的项目,也就是说c/c++需要先编译成LLVM的中间代码后,再转为JS代码。

换句话说,理论上所有可以转为LLVM的语言,都可以转为asm.js。

LLVM是什么?

LVM是构架编译器(compiler)的框架系统,LLVM 命名最早源自于底层虚拟机(Low Level Virtual Machine);

https://llvm.org/

架构编译器又是个啥玩意?

一个完整的编译器架构,c/c++的开发程序员熟知的编译器有gcc/g++之类的,而常见的这类预编译器的架构如下:
请添加图片描述

整个编译器的三大部分耦合比较紧密,这就导致gcc一般只能对c/c++进行编译;

而LLVM是个架构编译器,他的编译组成是有一系列组件组成,其架构如下所示:

请添加图片描述前后端是由上框架可知是一种轻松耦合的架构,前端后端统一使用一层中间代码及上图的LLVM IR(LLVM Intermediate Representation)进行交流;这种架构的好处是:

当我们需要支持一种语言的时候,我们只需要拓展一个前端编译器即可,而当我们需要支持一种新的设备或平台时,我们只需要拓展一个后端编译器即可,这就是架构编译器。

什么是Clang?

Clang是LLVM的一个子项目,它是LLVM架构的C/C++/Objective-C编译器前端;正如前面的LLVM架构所描述的一样,Clang是个编译器前端,用于将源代码转为LLVM IR中间代码,其主要流程如下:

源码(C/C++) -> 词法分析 -> 语法分析 -> 语义分析 -> 生成中间代码(LLVM IR)

Clang的重要的特性是编译快速、占内存少,而代码质量还比GCC来得高;此外Clang有一个重要的衍生项目是静态分析工具,能够通过自动分析程序的逻辑,在编译时就找出程序可能的bug,这个功能叫做ARC。

Clang相比GCC有许多优势,可到官网了解更多 https://clang.llvm.org/

至此,我们完成是Emscripten编译流程的所需要的背景知识及可能涉及到的名称,于是我们终于可以开始介绍Emscripten
尼马,都睡着了 还没开始?我还以为快结束了@v@

Emscripten编译流程

实际上经过前面的结束,Emscripten的工作机制已经比较清晰了;就是将C/C++源码转为LLVM IR中间代码,再把中间代码转为asm.js或者WebAssembly二进制即wasm。Emscripten其工作流程为:

C/C++ -> LLVM -> Emscripten -> JavaScript/HTML

所以Emscripten是一基于LLVM架构的编译器,主要是在编译器后端进行处理,原本LLVM架构是将中件代码转为机器相关的目标代码,而Emscripten则是生成一种平台相关的目标代码,这种目标代码即asm.jsWebAssembly(wasm)

整个Emscripten的编译执行流程如下所示:

在这里插入图片描述

asm.js可采用WebGL进行加速执行,这也就是为什么asm.js执行会快很多!

猜你喜欢

转载自blog.csdn.net/youlinhuanyan/article/details/128758925
今日推荐