美文网首页我爱编程
asm.js和Emscripten

asm.js和Emscripten

作者: Kris_lee | 来源:发表于2017-10-31 14:57 被阅读111次

    2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时突发奇想:许多 3D 游戏都是用 C / C++ 语言写的,如果能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。

    于是,他开始研究怎么才能实现这个目标,为此专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JS 代码,但不是普通的 JS,而是一种叫做 asm.js 的 JavaScript 变体。

    关于asm.js的理解

    )

    asm.js的简介

    asm.js是一个中间语言被设计用于如C运行Web应用程序的。同时保持性能能够高于标准的JS的计算机软件。

    对于传统而言,C/C++编译成JS有两个最大的困难。

    C / C++ 是静态类型语言,而 JS 是动态类型语言
    C / C++ 是手动内存管理,而 JS 依靠垃圾回收机制

    这里需要说明的是,C是弱类型静态语言。而js是弱类型动态语言。关于这方面也查阅了一部分资料。
    关于强弱类型的图

    简而言之:

    前两者,弱/强类型指的是语言类型系统的类型检查的严格程度。后两者指的是变量与类型的绑定方法。

    弱类型相对于强类型来说类型检查更不严格,比如说允许变量类型的隐式转换,允许强制类型转换等等。强类型语言一般不允许这么做。
    静态类型指的是编译器在compile time执行类型检查,动态类型指的是编译器(虚拟机)在runtime执行类型检查。简单地说,在声明了一个变量之后,不能改变它的类型的语言,是静态语言;能够随时改变它的类型的语言,是动态语言。因为动态语言的特性,一般需要运行时虚拟机支持。

    asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。

    一旦 JavaScript 引擎发现运行的是 asm.js,就知道这是经过优化的代码,可以跳过语法分析这一步,直接转成汇编语言。另外,浏览器还会调用 WebGL 通过 GPU 执行 asm.js,即 asm.js 的执行引擎与普通的 JavaScript 脚本不同。这些都是 asm.js 运行较快的原因。据称,asm.js 在浏览器里的运行速度,大约是原生代码的50%左右。

    值得注意的是
    asm.js 没有垃圾回收机制,所有内存操作都由程序员自己控制。asm.js 通过 TypedArray 直接读写内存。

    var buffer = new ArrayBuffer(32768);
    var HEAP8 = new Int8Array(buffer);
    function compiledCode(ptr) {
      HEAP[ptr] = 12;
      return HEAP[ptr + 4];
    }  
    

    如果设计到指针,也是一样处理。

    size_t strlen(char *ptr) {
      char *curr = ptr;
      while (*curr != 0) {
        curr++;
      }
      return (curr - ptr);
    }
    

    上面代码编译成asm.js.就是下面这样。

    function strlen(ptr) {
      ptr = ptr|0;
      var curr = 0;
      curr = ptr;
      while (MEM8[curr]|0 != 0) {
        curr = (curr + 1)|0;
      }
      return (curr - ptr)|0;
    }
    

    Emscripten 编译器

    虽然 asm.js 可以手写,但是它从来就是编译器的目标语言,要通过编译产生。目前,生成 asm.js 的主要工具是 Emscripten。

    emscripten-图

    Emscripten 的底层是 LLVM 编译器,理论上任何可以生成 LLVM IR(Intermediate Representation)的语言,都可以编译生成 asm.js。 但是实际上,Emscripten 几乎只用于将 C / C++ 代码编译生成 asm.js

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

    图解asm.js的过程

    hello world

    #include <iostream>
    
    int main() {
      std::cout << "Hello World!" << std::endl;
    }
    

    然后,将这个程序转成asm.js

    $ emcc hello.cc
    $ node a.out.js
    Hello World!
    

    上面代码中,emcc命令用于编译源码,默认生成a.out.js。使用 Node 执行a.out.js,就会在命令行输出 Hello World。
    注意,asm.js 默认自动执行main函数。
    emcc是 Emscripten 的编译命令。它的用法非常简单。

    asm.js的用途

    asm.js 不仅能让浏览器运行 3D 游戏,还可以运行各种服务器软件,比如 Lua、Ruby 和 SQLite。 这意味着很多工具和算法,都可以使用现成的代码,不用重新写一遍。
    另外,由于 asm.js 的运行速度较快,所以一些计算密集型的操作(比如计算 Hash)可以使用 C / C++ 实现,再在 JS 中调用它们。

    真实的转码实例可以看一下 gzlib 的编译,参考它的 Makefile 怎么写。

    参考链接

    wikipedia - asm.js

    wiki地址-Strong typing

    wiki地址-Type system

    阮一峰老师的网络日志

    知乎关于弱类型,强类型,动态,静态的区别--作者:Alan Li

    Asm.js: The JavaScript Compile Target, by John Resig

    Emscripten & asm.js: C++'s role in the modern web, by Alon Zakai

    An Introduction to Web Development with Emscripten, by Charles Ofria

    相关文章

      网友评论

        本文标题:asm.js和Emscripten

        本文链接:https://www.haomeiwen.com/subject/nxebpxtx.html