やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

AssemblyScriptをインストールする

 TypeScript風に書いてWASMファイルを出力する。

成果物

情報源

前提

手順

sudo npm install -g assemblyscript 

バージョン確認

asc --version
Version 0.20.3

 ascコマンドが遅い。10秒以上かかる。これはひどい。バージョン値を返すのに10秒もかかったのはお前がはじめてだ。ノロマ大賞をくれてやる。本当にこれを使ったら速くなるのか? すでにお前が遅い件。説得力なさすぎィ!

$ time asc --version
Version 0.20.3

real    0m10.888s
user    0m36.016s
sys 0m1.884s

ヘルプ確認

asc --help

ヘルプ内容

SYNTAX
  asc [entryFile ...] [options]

EXAMPLES
  asc hello.ts
  asc hello.ts -o hello.wasm -t hello.wat
  asc hello1.ts hello2.ts -o -O > hello.wasm
  asc --config asconfig.json --target release

OPTIONS

 General

  --version, -v         Prints just the compiler's version and exits.
  --help, -h            Prints this message and exits.
  --config              Configuration file to apply. CLI arguments take precedence.
  --target              Configuration file target to use. Defaults to 'release'.

 Optimization

  --optimize, -O        Optimizes the module. Typical shorthands are:
                        
                         Default optimizations   -O
                         Make a release build    -O --noAssert
                         Make a debug build      --debug
                         Optimize for speed      -Ospeed
                         Optimize for size       -Osize
                        
  --optimizeLevel       How much to focus on optimizing code. [0-3]
  --shrinkLevel         How much to focus on shrinking code size. [0-2, s=1, z=2]
  --converge            Re-optimizes until no further improvements can be made.
  --noAssert            Replaces assertions with just their value without trapping.

 Output

  --outFile, -o         Specifies the WebAssembly output file (.wasm).
  --textFile, -t        Specifies the WebAssembly text output file (.wat).
  --bindings, -b        Specifies the bindings to generate (.js + .d.ts).
                        
                          esm  JavaScript bindings & typings for ESM integration.
                          raw  Like esm, but exports just the instantiate function.
                               Useful where modules are meant to be instantiated
                               multiple times or non-ESM imports must be provided.

 Debugging

  --sourceMap           Enables source map generation. Optionally takes the URL
                        used to reference the source map from the binary file.
  --debug               Enables debug information in emitted binaries.

 Features

  --importMemory        Imports the memory from 'env.memory'.
  --noExportMemory      Does not export the memory as 'memory'.
  --initialMemory       Sets the initial memory size in pages.
  --maximumMemory       Sets the maximum memory size in pages.
  --sharedMemory        Declare memory as shared. Requires maximumMemory.
  --zeroFilledMemory    Assume imported memory is zeroed. Requires importMemory.
  --importTable         Imports the function table from 'env.table'.
  --exportTable         Exports the function table as 'table'.
  --exportStart         Exports the start function using the specified name instead
                        of calling it implicitly. Useful for WASI or to obtain the
                        exported memory before executing any code accessing it.
  --runtime             Specifies the runtime variant to include in the program.
                        
                         incremental  TLSF + incremental GC (default)
                         minimal      TLSF + lightweight GC invoked externally
                         stub         Minimal runtime stub (never frees)
                         ...          Path to a custom runtime implementation
                        
  --exportRuntime       Exports the runtime helpers (__new, __collect etc.).
  --stackSize           Overrides the stack size. Only relevant for incremental GC
                        or when using a custom runtime that requires stack space.
                        Defaults to 0 without and to 16384 with incremental GC.
  --enable              Enables WebAssembly features being disabled by default.
                        
                         threads             Threading and atomic operations.
                         simd                SIMD types and operations.
                         reference-types     Reference types and operations.
                         gc                  Garbage collection (WIP).
                        
  --disable             Disables WebAssembly features being enabled by default.
                        
                         mutable-globals     Mutable global imports and exports.
                         sign-extension      Sign-extension operations
                         nontrapping-f2i     Non-trapping float to integer ops.
                         bulk-memory         Bulk memory operations.
                        
  --use, -u             Aliases a global object under another name, e.g., to switch
                        the default 'Math' implementation used: --use Math=JSMath
                        Can also be used to introduce an integer constant.
  --lowMemoryLimit      Enforces very low (<64k) memory constraints.

 Linking

  --memoryBase          Sets the start offset of emitted memory segments.
  --tableBase           Sets the start offset of emitted table elements.

 API

  --transform           Specifies the path to a custom transform to load.

 Binaryen

  --trapMode            Sets the trap mode to use.
                        
                         allow  Allow trapping operations. This is the default.
                         clamp  Replace trapping operations with clamping semantics.
                         js     Replace trapping operations with JS semantics.
                        
  --runPasses           Specifies additional Binaryen passes to run after other
                        optimizations, if any. See: Binaryen/src/passes/pass.cpp
  --noValidate          Skips validating the module using Binaryen.

 Other

  --baseDir             Specifies the base directory of input and output files.
  --noColors            Disables terminal colors.
  --noUnsafe            Disallows the use of unsafe features in user code.
                        Does not affect library files and external modules.
  --noEmit              Performs compilation as usual but does not emit code.
  --showConfig          Print computed compiler options and exit.
  --stats               Prints statistics on I/O and compile times.
  --pedantic            Make yourself sad for no good reason.
  --lib                 Adds one or multiple paths to custom library components and
                        uses exports of all top-level files at this path as globals.
  --path                Adds one or multiple paths to package resolution, similar
                        to node_modules. Prefers an 'ascMain' entry in a package's
                        package.json and falls back to an inner 'assembly/' folder.
  --wasm                Uses the specified Wasm binary of the compiler.
  -- ...                Specifies node.js options (CLI only). See: node --help

コードを書く

hello.ts

export function increment(num: u32) : u32 {
  return num + 1;
}

ビルドする

 テキスト形式とバイナリ形式の二種類ある。それぞれ以下のように出力する。

asc hello.ts >| hello.wat 
asc -o hello.wasm hello.ts

 出力内容を確認してみる。

cat hello.wasm
(module
 (type $i32_=>_i32 (func (param i32) (result i32)))
 (global $~lib/memory/__data_end i32 (i32.const 8))
 (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16392))
 (global $~lib/memory/__heap_base i32 (i32.const 16392))
 (memory $0 0)
 (table $0 1 funcref)
 (elem $0 (i32.const 1))
 (export "increment" (func $hello-ok/increment))
 (export "memory" (memory $0))
 (func $hello-ok/increment (param $0 i32) (result i32)
  local.get $0
  i32.const 1
  i32.add
 )
)
cat hello.wasm
asm`pA
     A��
        A��
            incrementmemory A

     Aj
           

 遅い。上記2ビルドするのに30秒くらいかかった。

$ time ./build.sh 

real    0m24.133s
user    1m15.491s
sys 0m4.085s

 つぎはwasmファイルをjsから呼び出す。

wasmをjsから呼出

  • hello.wasm
  • load-wasm.js
  • index.html

load-wasm.js

WebAssembly.instantiateStreaming(fetch("hello.wasm"), {}).then(mod => {
  const increment = mod.instance.exports.increment;
  const result = increment(999);
  console.log(result);
  console.assert(result === 1000);
  document.querySelector('body').innerText = result;
});

 実装したincrement関数を呼び出していることがわかる。

index.html

<script src="load-wasm.js"></script>

 これを実行すのだが、fetch APIを使っている。こいつがあるせいでローカルサーバが必要。

 index.htmlがあるパスまでカレントディレクトリを移動させ、以下のコマンドを実行する。

server.sh

#!/bin/bash
python3 -m http.server 8000 &
sleep 1
URL=http://0.0.0.0:8000/
echo "$URL" | xsel -bi 
chromium-browser "$URL"

 つぎにブラウザでhttp://0.0.0.0:8000/にアクセスする。HTMLに1000と表示されたらOK。DEMOをみればいいよ。

蛇足

エラー

 ちなみに前回のTypeScriptのコードのままビルドしようとするとエラーになった。

ERROR TS1110: Type expected.

 function increment(num: number) {
                                ^
 in hello-ok.ts(1,32)

FAILURE 1 parse error(s)
ERROR TS1110: Type expected.

 function increment(num: number) {
                                ^
 in hello-ng.ts(1,32)

FAILURE 1 parse error(s)

 コードを見比べてみると別物だということがよくわかる。

TypeScript

function increment(num: number) {
  return num + 1;
}
console.log(increment(999));

AssemblyScript

export function increment(num: u32) : u32 {
  return num + 1;
}

騙された

 「は? AsseblyScriptってTypeScriptからwasmファイルにビルドするコンパイラじゃなかったのかよ!」とキレそうになった。騙された。だれだよ、こんな嘘ついたやつ。全然コード別ものやんけ!

 まーた簡単詐欺かよ。「簡単だよ〜」という甘い言葉に誘われて騙されたのは何度目だろうか。いいかげんにしてくれ。はい、勝手に勘違いした私が悪いんですよね。知ってます。

所感

 ビルドに時間がかかる。これはしょうがない。

 TypeScriptとは別の型名である。つまり新たな学習コストがかかるということだ。

 なんにせよ、複雑な計算をするときはAssemblyScriptでコードを書いてバイナリ化すれば高速化できる。それをJSで呼び出す処理を書かねばならないのが面倒だし、fetch APIのせいでローカルサーバも必要だから動作確認も面倒だが。

対象環境

$ uname -a
Linux raspberrypi 5.10.63-v7l+ #1496 SMP Wed Dec 1 15:58:56 GMT 2021 armv7l GNU/Linux