iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 3
0
自我挑戰組

WebAssembly + Rust 的前端應用系列 第 3

[Day 3] Webassembly Hello World!

大家好,相信各位對於 webassembly 已經有一些基礎的認知了今天就來不免俗的就來做一下官網的 Hello world。

另外筆者的開發環境是 Mac OS 如果其他系統要安裝請參考這邊

下載編譯工具

clone emsdk git 專案

$ git clone https://github.com/emscripten-core/emsdk.git

安裝 sdk

$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest

這邊稍為講一下 emsdk 先引一段官網的說明

Emscripten is a toolchain for compiling to asm.js and WebAssembly, built using LLVM, that lets you run C and C++ on the web at near-native speed without plugins.

簡單來說所以就是把 C/C++ 編譯成 asm.js 以及 WebAssembly 的工具集。

環境變數設定

$ source ./emsdk_env.sh --build=Release

Note: 這邊的環境變數在 terminal 重啟之後便會失效,若想要永久的設定請添加至 .bashrc 或 .zshrc

例如添加變數至 .zshrc

# emsdk
export PATH=/Users/liyanxin/Life/myprojects/emsdk:$PATH
export PATH=/Users/liyanxin/Life/myprojects/emsdk/fastcomp/emscripten:$PATH
export PATH=/Users/liyanxin/Life/myprojects/emsdk/node/12.9.1_64bit/bin:$PATH

編譯並且執行

新增專案資料夾

$ mkdir hello && cd hello

建立主要 function

$ cat << EOF > hello.c
#include <stdio.h>
int main(int argc, char ** argv) {
  printf("Hello, world!\n");
}
EOF

編譯檔案

$ emcc hello.c -o hello.html

啟動本地伺服器

$ emrun --no_browser --port 8080 .

http://localhost:8080 查看網頁

這樣就完成了一個簡單的專案,但是當我們去檢視 build 出來的檔案的時候我們會發現不是只有一個 hello.wasm 的檔案居然還有一個 hello.js 的檔案,那麼這個檔案是做什麼的呢?

原來當初 Mozilla 異想天開為了解決網頁遊戲的效能問題而發展出一套方法可以把 C/C++ 編譯成 javascript,也就是後來的 asm.js。
而其原理基本上就是把 JS 限制成靜態語言的寫法然後當 JS 引擎接到這類型的檔案之後可以跳過語法分析讓執行效能更好。
但是 asm.js 本質上還是 javascript 因此在檔案大小和解析速度上都比不上 wasm。

那麼這個 hello.js 其實就是當初設計拿來載入 asm.js 的程式而現在也可以用在載入 wasm 上面,筆者相信隨著未來 wasm 越來越穩定會有更簡單更 native 的方法載入 wasm。

下面就介紹一下另外一個透過瀏覽器內建的方式載入就無需上面提到的載入 wasm 的程式。

檔案名稱:hello_world.c

int doubler(int x) {
  return 2 * x;
}

檔案名稱:hello_world.html

<html>
<head>
  <script>
    // Check for wasm support.
    if (!('WebAssembly' in window)) {
      alert('you need a browser with wasm support enabled :(');
    }
    // Loads a WebAssembly dynamic library, returns a promise.
    // imports is an optional imports object
    function loadWebAssembly(filename, imports) {
      // Fetch the file and compile it
      return fetch(filename)
        .then(response => response.arrayBuffer())
        .then(buffer => WebAssembly.compile(buffer))
        .then(module => {
          // Create the imports for the module, including the
          // standard dynamic library imports
          imports = imports || {};
          imports.env = imports.env || {};
          imports.env.__memory_base = imports.env.__memory_base || 0;
          imports.env.__table_base = imports.env.__table_base || 0;
          if (!imports.env.memory) {
            imports.env.memory = new WebAssembly.Memory({ initial: 256 });
          }
          if (!imports.env.table) {
            imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' }  );
          }
          // Create the instance.
          return new WebAssembly.Instance(module, imports);
        });
    }
    // Main part of this example, loads the module and uses it.
    loadWebAssembly('hello_world.wasm')
      .then(instance => {
        var exports = instance.exports; // the exports of that instance
        var doubler = exports._doubler; // the "doubler" function (note "_" prefix)
        // now we are ready, set up the button so the user can run the code
        var button = document.getElementById('run');
        button.value = 'Call a method in the WebAssembly module';
        button.addEventListener('click', function() {
          var input = 21;
          alert(input + ' doubled is ' + doubler(input));
        }, false);
      }
    );
  </script>
</head>
<body>
  <input type="button" id="run" value="(waiting for WebAssembly)"/>
</body>
</html>

編譯

$ emcc hello_world.c -Os -s WASM=1 -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS='["_doubler"]' -o hello_world.wasm

執行

$ emrun --no_browser --port 8080 .

最後一樣有問題歡迎發問

/images/emoticon/emoticon07.gif

參考網站

了解 Emscripten

asmjs

webassembly getting start


上一篇
[Day 2] Webassembly 介紹
下一篇
[Day 4] Rust 介紹 重頭戲登場!
系列文
WebAssembly + Rust 的前端應用30

尚未有邦友留言

立即登入留言