Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Art of WebAssembly #33

Merged
merged 50 commits into from
Aug 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
986cb85
Chapter 1
zaki-yama Jun 14, 2021
f171825
[Ch02] Hello World in WebAssembly
zaki-yama Jun 16, 2021
c25d9f4
[ch02] WAT Variables > Global Variables and Type Conversion
zaki-yama Jun 17, 2021
d296122
[ch02] WAT Variables > Local Variables
zaki-yama Jun 18, 2021
9e118c0
[ch02] WAT Variables > Unpacking S-Expressions
zaki-yama Jun 18, 2021
a9ab9e1
[ch03] Writing an is_prime Function
zaki-yama Jun 18, 2021
eb2a42e
[ch03] ACCESSING THE STACK FROM A FUNCTION
zaki-yama Jun 18, 2021
d00353d
[ch03] Performance Implications of External Function Calls
zaki-yama Jun 18, 2021
6c198dd
[ch03] Function Tables
zaki-yama Jun 21, 2021
0ff5625
[ch05] Passing the String Length to JavaScript
zaki-yama Jun 24, 2021
6f039a5
[ch05] Null-Terminated Strings
zaki-yama Jun 24, 2021
98acfe4
[ch05] Length-Prefixed Strings
zaki-yama Jun 28, 2021
e9752e1
[ch05] Copying Strings > Byte-by-Byte Copy
zaki-yama Jun 28, 2021
6a08dbb
[ch05] Copying Strings > 64-Bit Copy
zaki-yama Jun 28, 2021
0a8471b
[ch05] Copying Strings > Combination Copy Function
zaki-yama Jun 30, 2021
88e003d
[ch05] Creating Number Strings [skip]
zaki-yama Jul 1, 2021
65808c3
[ch06] Linear Memory in WebAssembly
zaki-yama Jul 2, 2021
36e2c60
[ch06] JavaScript Memory Object > Creating the WebAssembly Memory Object
zaki-yama Jul 4, 2021
141badf
[ch06] JavaScript Memory Object > Logging to the Console with Colors
zaki-yama Jul 4, 2021
0ce46d0
[ch06] JavaScript Memory Object > Creating the JavaScript in store_da…
zaki-yama Jul 4, 2021
7e50bf1
[ch06] JavaScript Memory Object (memo)
zaki-yama Jul 4, 2021
5140c1c
[ch06] Collision Detection
zaki-yama Jul 4, 2021
850954c
[ch07] Setting Up a Simple Node Server
zaki-yama Jul 5, 2021
1709e68
[ch07] Our First WebAssembly Web Application
zaki-yama Jul 5, 2021
7d7d907
[ch07] Hex and Binary Strings
zaki-yama Jul 5, 2021
f192883
[ch07] memo
zaki-yama Jul 5, 2021
dea32fb
[ch08] Rendering to the Canvas
zaki-yama Jul 6, 2021
bb6e4e5
[ch08] The WAT Module > Imported Values
zaki-yama Jul 9, 2021
e6eacf1
[ch08] The WAT Module > Clearing the Canvas
zaki-yama Jul 9, 2021
a78d7ad
[ch08] The WAT Module > Absolute Value Function
zaki-yama Jul 9, 2021
97f34ee
[ch08] The WAT Module > Setting a Pixel Color
zaki-yama Jul 9, 2021
e37e043
[ch08] The WAT Module > Drawing the Object
zaki-yama Jul 9, 2021
f6ecf36
[ch08] The WAT Module > Setting and Getting Object Attributes
zaki-yama Jul 15, 2021
d6cc073
[ch08] The WAT Module > The $main function
zaki-yama Jul 20, 2021
e7c2805
[ch08] Summary
zaki-yama Jul 20, 2021
dfb1504
[ch09] Using a Profiler > Chrome Profiler
zaki-yama Jul 21, 2021
cd27fe5
[ch09] wasm-opt
zaki-yama Jul 24, 2021
565a2b0
[ch09] Strategies for Improving Performance
zaki-yama Jul 24, 2021
3f00f84
[ch09] Comparing the Collision Detection App with JavaScript
zaki-yama Jul 24, 2021
db6bcae
[ch09] Hand Optimizing WAT
zaki-yama Jul 27, 2021
32a5b3f
[ch09] Logging Performance
zaki-yama Jul 27, 2021
f0a87c0
[ch09] More Sophisticated Testing with benchmark.js
zaki-yama Jul 27, 2021
e8ce2f5
[ch09] Comparing WebAssembly and JavaScript with `--print-bytecode`
zaki-yama Jul 27, 2021
5748063
[ch10] Debugging from the Console
zaki-yama Jul 28, 2021
fcdb608
[ch10] Stack Trace
zaki-yama Jul 28, 2021
3da8c23
[ch11] AssemblyScript CLI
zaki-yama Jul 28, 2021
505c827
[ch11] Hello World AssemblyScript
zaki-yama Jul 28, 2021
673dcb5
[ch11] Object Oriented Programming in AssemblyScript
zaki-yama Jul 28, 2021
b542dfd
[ch03] memo
zaki-yama Jul 29, 2021
d3682c3
memo
zaki-yama Aug 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions the-art-of-webassembly/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vscode
20 changes: 20 additions & 0 deletions the-art-of-webassembly/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## 疑問

- stack と local variable や global variable が格納される領域って違うの?
- ch06
- `(global $obj_base_addr (import "env" "obj_base_addr") i32)`
- これで import した値がセットされるんだっけ? global 変数の基本的な構文の話
- ch07
- `instantiate``instantiateStreaming` の違い (Node.js は前者しか使えない)
- ch08
- `i32.load``i32.store` ってどういう動きになるんだっけ?

## 学び

### Chapter 2 WebAssembly Text Basics

- importObject の第一階層、`env` である必要ないんだ
- Rust で書く場合はどうかな
- WAT は S 式 と linear instruction set の 2 つの書き方がある。混在もできる

### Chapter 3 Functions And Tables
11 changes: 11 additions & 0 deletions the-art-of-webassembly/ch01/AddInt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs = require("fs");

const bytes = fs.readFileSync(__dirname + "/AddInt.wasm");
const value_1 = parseInt(process.argv[2]);
const value_2 = parseInt(process.argv[3]);

(async () => {
const obj = await WebAssembly.instantiate(new Uint8Array(bytes));
let add_value = obj.instance.exports.AddInt(value_1, value_2);
console.log(`${value_1} + ${value_2} = ${add_value}`);
})();
Binary file added the-art-of-webassembly/ch01/AddInt.wasm
Binary file not shown.
9 changes: 9 additions & 0 deletions the-art-of-webassembly/ch01/AddInt.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(module
(func (export "AddInt")
(param $value_1 i32) (param $value_2 i32)
(result i32)
local.get $value_1
local.get $value_2
i32.add
)
)
Binary file added the-art-of-webassembly/ch01/file.wasm
Binary file not shown.
1 change: 1 addition & 0 deletions the-art-of-webassembly/ch01/file.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(module)
100 changes: 100 additions & 0 deletions the-art-of-webassembly/ch02/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Chapter 2 WebAssembly Text Basics

この Chapter で紹介すること

- 2 comment styles in WebAssembly
- 伝統的な hello world アプリケーション
- 文字列扱うのが難しいので、いきなり hello world はやらない
- import object を使って JS から Wasm にデータをインポートする方法
- named and unnamed global and local variables
- S 式と、wat2wasm コンパイラーが S 式をどうアンパックするか
- if/else , branch tables, loops, blocks

## Writing the Simplest Module

すべての WAT アプリケーションはモジュールでなければならない。よって最初は module シンタックスから始まる

```wat
(module
;; This is where the module code goes.
)
```

- 複数行コメントは `(; ... ;)`

## Hello World in WebAssembly

### Creating Our Wat Module

- linear memory

```wat
(module
(import "env" "print_string" (func $print_string( param i32 )))
(import "env" "buffer" (memory 1))
)
```

- `(memory 1)` は buffer が linear memory 1 ページになることを示している
- page とは
- linear memory に一度に割り当てることのできる最小のメモリの塊
- Wasm では 1 ページ 64KB

以下はスキップ

- if/else Conditional Logic
- Loops and Blocks
- The loop Expression

## WAT Variables

### Unpacking S-Expressions

```wat
(i32.mul
(i32.add
(i32.const 3)
(i32.const 2)
)
(i32.sub
(i32.const 9)
(i32.const 7)
)
)
```


```wat
i32.const 3
i32.const 2
i32.add
i32.const 9
i32.const 7
i32.sub
i32.mul
```

と等価

## Loops and Blocks

### The `block` Statement

```wat
;; This code is for demonstration and not part of a larger app
(block $jump_to_end
br $jump_to_end
;; code below the branch does not execute. br jumps to the end of the block
nop
)
;; This is where the br statement jumps to
nop
```

- `br``block` を抜ける (the code can only jump to the end of a `block` if it’s inside that `block`)

## The loop Expression
10 changes: 10 additions & 0 deletions the-art-of-webassembly/ch02/SumSquared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const fs = require("fs");
const bytes = fs.readFileSync(__dirname + "/SumSquared.wasm");
const val1 = parseInt(process.argv[2]);
const val2 = parseInt(process.argv[3]);

(async () => {
const obj = await WebAssembly.instantiate(new Uint8Array(bytes));
let sum_sq = obj.instance.exports.SumSquared(val1, val2);
console.log(`(${val1} + ${val2}) * (${val1} + ${val2}) = ${sum_sq}`);
})();
Binary file added the-art-of-webassembly/ch02/SumSquared.wasm
Binary file not shown.
12 changes: 12 additions & 0 deletions the-art-of-webassembly/ch02/SumSquared.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(module
(func (export "SumSquared")
(param $value_1 i32) (param $value_2 i32)
(result i32)
(local $sum i32)

(i32.add (local.get $value_1) (local.get $value_2))
local.set $sum

(i32.mul (local.get $sum) (local.get $sum))
)
)
30 changes: 30 additions & 0 deletions the-art-of-webassembly/ch02/Unpacking-S-Expressions.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
(i32.mul ;; executes 7th (last)
(i32.add ;; executes 3rd
(i32.const 3) ;; executes 1st
(i32.const 2) ;; executes 2nd
)
(i32.sub ;; executes 6th
(i32.const 9) ;; executes 4th
(i32.const 7) ;; executes 5th
)
)

(;
(i32.add ;; executes 3rd
(i32.const 3) ;; executes 1st
(i32.const 2) ;; executes 2nd
)
i32.const 3
i32.const 2
i32.add
と等価
;)
i32.const 3 ;; Stack = [3]
i32.const 2 ;; Stack = [2, 3]
i32.add ;; 2 & 3 popped from stack, added sum of 5 pushed onto stack [5]

i32.const 9 ;; Stack = [9,5]
i32.const 7 ;; Stack = [7,9,5]
i32.sub ;; 7 & 9 popped off stack . 9-7=2 pushed on stack [2,5]
i32.mul ;; 2,5 popped off stack, 2x5=10 is pushed on the stack [10]
28 changes: 28 additions & 0 deletions the-art-of-webassembly/ch02/globals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const fs = require("fs");
const bytes = fs.readFileSync(__dirname + "/globals.wasm");
let global_test = null;

let importObject = {
js: {
log_i32: (value) => {
console.log("i32: ", value);
},
log_f32: (value) => {
console.log("f32: ", value);
},
log_f64: (value) => {
console.log("f64: ", value);
},
},
env: {
import_i32: 5_000_000_000,
import_f32: 123.0123456789,
import_f64: 123.0123456789,
},
};

(async () => {
let obj = await WebAssembly.instantiate(new Uint8Array(bytes), importObject);
({ globaltest: global_test } = obj.instance.exports);
global_test();
})();
Binary file added the-art-of-webassembly/ch02/globals.wasm
Binary file not shown.
15 changes: 15 additions & 0 deletions the-art-of-webassembly/ch02/globals.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(module
(global $import_integer_32 (import "env" "import_i32") i32)
(global $import_float_32 (import "env" "import_f32") f32)
(global $import_float_64 (import "env" "import_f64") f64)

(import "js" "log_i32" (func $log_i32 (param i32)))
(import "js" "log_f32" (func $log_f32 (param f32)))
(import "js" "log_f64" (func $log_f64 (param f64)))

(func (export "globaltest")
(call $log_i32 (global.get $import_integer_32))
(call $log_f32 (global.get $import_float_32))
(call $log_f64 (global.get $import_float_64))
)
)
24 changes: 24 additions & 0 deletions the-art-of-webassembly/ch02/helloworld.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const fs = require("fs");
const bytes = fs.readFileSync(__dirname + "/helloworld.wasm");

let hello_world = null; // function will be set later
let start_string_index = 100; // linear memory location of string
let memory = new WebAssembly.Memory({ initial: 1 }); // linear memory

let importObject = {
env: {
buffer: memory,
start_string: start_string_index,
print_string: function (str_len) {
const bytes = new Uint8Array(memory.buffer, start_string_index, str_len);
const log_string = new TextDecoder("utf8").decode(bytes);
console.log(log_string);
},
},
};

(async () => {
let obj = await WebAssembly.instantiate(new Uint8Array(bytes), importObject);
({ helloworld: hello_world } = obj.instance.exports);
hello_world();
})();
Binary file added the-art-of-webassembly/ch02/helloworld.wasm
Binary file not shown.
12 changes: 12 additions & 0 deletions the-art-of-webassembly/ch02/helloworld.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(module
;; param i32: length of string
(import "env" "print_string" (func $print_string(param i32) ))
;; memory 1: allocate 1 page(smallest chunk of memory) of linear memory
(import "env" "buffer" (memory 1))
(global $start_string (import "env" "start_string") i32)
(global $string_len i32 (i32.const 12))
(data (global.get $start_string) "hello world!")
(func (export "helloworld")
(call $print_string (global.get $string_len))
)
)
63 changes: 63 additions & 0 deletions the-art-of-webassembly/ch03/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Chapter 3 Functions and Tables

この Chapter で学ぶこと

- WebAssembly の関数について学ぶ
- いつ、どのように JS や他の WebAssembly モジュールから関数を import するのか
- どのように WebAssembly の関数を export するのか
- table について

<!-- TOC -->

- [When to Call Functions from WAT](#when-to-call-functions-from-wat)
- [Writing an `is_prime` Function](#writing-an-is_prime-function)
- [Passing Parameters](#passing-parameters)
- [Declaring an Imported Function](#declaring-an-imported-function)
- [Performance Implications of External Function Calls](#performance-implications-of-external-function-calls)
- [Function Tables](#function-tables)

<!-- /TOC -->

## When to Call Functions from WAT

- `(export)`
- JS から Wasm の関数の呼び出しはオーバーヘッドがあるので、小さなタスクを実行するだけの関数であれば export すべきでないよ。JS でやった方がいい
- Wasm の関数は大量のデータを何回もループで処理するようなものに適している
- デバッグについては Chapter 10
- パフォーマンスチューニングの過程で、元々関数として切り出してたものをインライン化することもあるかもしれない
- パフォーマンス・チューニングについて詳しくは Chapter 9 で

## Writing an `is_prime` Function

### Passing Parameters

- `local.tee`: The local.tee command is like the local.set command in that it sets the value of the variable you pass to it to the value on top of the stack

## Declaring an Imported Function

### Performance Implications of External Function Calls

- "When you call a JavaScript function
in WAT, you lose some cycles to overhead. This number isn’t extremely large, but if you execute an external JavaScript function in a loop that iterates 4,000,000 times, it can add up."
- 2 つのベンチマーク比較
1. 内部で Wasm の関数を 4_000_000 回呼び出し
2. 内部で JS から import した関数を 4_000_000 回呼び出し

### Function Tables

- "Currently, tables only support the anyfunc type (anyfunc is a generic WebAssembly function type), but in the future they might support JavaScript objects and DOM elements as well."
- "Unlike import objects, JavaScript and WebAssembly can dynamically change tables at runtime."
- function table 経由での関数呼び出しは indirect なコールになるためパフォーマンスコストがある
- "However, you cannot add a JavaScript function to a function table from within JavaScript. There is a WebAssembly.Table function set that allows you to set functions in a table, only with a function defined in a WebAssembly module. We can work around this restriction by importing the JavaScript function into a WebAssembly module and adding it to the table there."
- table_export.wat で一度 import して table にセットしてるのはこれが理由

```
js_table_test time=67
js_import_test time=52
wasm_table_test time=26
wasm_import_test time=19
```

- オチとしては
- JS より Wasm のほうが速い
- table 経由より直接呼び出しのほうが速い
17 changes: 17 additions & 0 deletions the-art-of-webassembly/ch03/fail.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
;; wat2wasm will fail
(module
(func $inner
(result i32)
(local $l i32)
;; 99 is on the stack in the calling function
local.set $l

i32.const 2
)
(func (export "main")
(result i32)

i32.const 99 ;; push 99 onto stack - [99]
call $inner ;; 99 is on the stack here
)
)
Loading