A WebAssembly
tale
2022

WebAssembly ?
Web + Assembleur ?
?!?!?
Nicolas Decoster
@ogadaki

Toulouse

"WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web."
portable
efficient
compilation to the web
webassembly.org :
fast!
safe!
?
"WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications."
webassembly.org (now) :
WebAssembly, pour faire quoi ?
jeux
édition de vidéo ou d'image
application musicale
reconnaissance d'image
VR et AR
visualisation et simulation scientifique
etc.

vote du logo :

1990
HTML
HTTP
JavaScript
Web 2.0
1995
1996
2002
Flash
contenu statique
en ligne
naviguable
contenu dynamique
omniprésent
orienté utilisateur
besoin de plus de dynamique côté navigateur
comment ?
standard : JavaScript
mal aimé et lent
mais...
nombreuses tentatives d'alternatives
mais...
Flash, Java, ActiveX, NaCL, SilverLight, Dart...
problèmes de sécurités, de standardisation, d'adoption...
aucune techno ne s'impose vraiment
flash a un certain succès
mais pas adopté complétement

?
2012
Alon Zakai - Mozilla
side project : compiler du C en JavaScript
Comment ? Avec clang et LLVM !
ça marche !


code
C ou C++
LLVM
bytcode
clang
code
JavaScript
emscripten

Mais le code JavaScript généré est lent
comment améliorer les performances ?
d'autres rejoignent le projet chez Mozilla
-> asm.js !
asm.js = "a highly optimizable subset of JavaScript
+ a very large array as memory "
bonnes performances !
premier test grandeur nature : un jeu, la demo de Citadel d'Epic (basé sur le moteur Unreal 3). Au bout d'une semaine ça compile en asm.js, ça tourne et c'est fluide.
function strlen(ptr) {
ptr = ptr|0;
var curr = 0;
curr = ptr;
while (MEM8[curr]|0 != 0) {
curr = (curr + 1)|0;
}
return (curr - ptr)|0;
}

code
C ou C++
LLVM
bytcode
clang
code
asm.js
emscripten
Milestone
Il est maintenant possible
de compiler du C/C++ pour le Web
performant
(dans certains navigateurs)
fonctionne dans tous les navigateurs
sans plugin
grâce à emscripten
grâce à asm.js
car ams.js = JS subset
bonus gratuit : c'est sécurisé
car dans le moteur JS
mai 2013
bases posées
asm.js est utilisable
aucun obstacle à une adoption large
preuve de concept fonctionnelle
Mozilla se tourne vers les autres acteurs pour lancer une nouvelle dynamique commune sur ces acquis
WebAssembly

crédit: @linclark code-cartoons.com
projet WebAssembly
feuille blanche
nouvelle techno dans la lignée d'asm.js
intégrant l'expérience des équipes derrière asm.js et de technos similaires
+ contraintes bien pensées
+ adoption large par les acteurs
= succès
+ retours d'expérience
april 2015 WebAssembly Community Group started
june 2015 The first public announcement
march 2016 Definition of core feature with multiple interoperable
implementations
october 2016 Browser Preview announced with multiple interoperable
implementations
march 2017 Firefox and Chrome support WebAssembly
webassembly.org :
WebAssembly,
pour les dévs
j'ai un code en C
je veux le compiler en WebAssembly
Comment ?

j'ai un code en Rust
je veux le compiler en WebAssembly
Comment ?

tooling de la communauté Rust
j'ai un code en C#
je veux le compiler en WebAssembly
Comment ?
Blazor
= prochaine version de .NET Core
.NET 5
j'ai un code en Go
je veux le compiler en WebAssembly
Comment ?
natif avec le compilateur
j'ai un code en Java
je veux le compiler en WebAssembly
Comment ?
Tu attends (ou pas)...
j'ai un code en Python
j'ai un code en etc.

https://github.com/appcypher/awesome-wasm-langs




2019
old
#include <stdio.h>
int main() {
printf("WebAssembly\n");
}
$ ls
cli.js cli.wasm main.c
$ emcc main.c -o cli.js
$ ls
cli.js cli.wasm main.c
$ node cli.js
WebAssembly
#include <stdio.h>
int main() {
printf("WebAssembly\n");
}
$ ls webapp.*
webapp.html webapp.js webapp.wasm
$ emcc main.c -o webapp.html

WebAssembly
$ ls webapp.*
webapp.html webapp.js webapp.wasm
$ firefox webapp.html

s'ocuppe du sale boulot à votre place
compilation du C
émission du binaire WebAssembly
création du code JavaScript nécessaire
ajout de dépendances (stdio...)
etc.
liens avec canvas et WebGL
Et sous le capot ?
un format binaire
moteur WebAssembly dédié dans le navigateur
interaction avec le moteur JavaScript
format WebAssembly
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "_Z4facti" (func $_Z4facti))
(func $_Z4facti (param $0 i32) (result f64)
(local $1 i64)
(local $2 i64)
(block $label$0
(br_if $label$0
(i32.lt_s
(get_local $0)
(i32.const 1)
)
)
(set_local $1
(i64.add
(i64.extend_s/i32
(get_local $0)
)
(i64.const 1)
)
)
(set_local $2
(i64.const 1)
)
(loop $label$1
(set_local $2
(i64.mul
(get_local $2)
(tee_local $1
(i64.add
(get_local $1)
(i64.const -1)
)
)
)
)
(br_if $label$1
(i64.gt_s
(get_local $1)
(i64.const 1)
)
)
)
(return
(f64.convert_s/i64
(get_local $2)
)
)
)
(f64.const 1)
)
)
0000000: 6100 6d73 0001 0000 0601 6001 7f01 7c01
0000010: 0203 0001 0404 7001 0000 0305 0001 0701
0000020: 0215 6d06 6d65 726f 0279 0800 5a5f 6634
0000030: 6361 6974 0000 3f0a 3d01 0201 027e 2040
0000040: 4100 4801 000d 0020 42ac 7c01 0121 0142
0000050: 0221 4003 0220 0120 7f42 227c 7e01 0221
0000060: 0120 0142 0d55 0b00 0220 0fb9 440b 0000
0000070: 0000 0000 3ff0 000b
0000000: 0061 736d # WASM_BINARY_MAGIC
0000004: 0100 0000 # WASM_BINARY_VERSION
# section "Type" (1)
0000008: 01 # section code
0000009: 00 # section size (guess)
000000a: 01 # num types
# type 0
000000b: 60 # func
000000c: 01 # num params
000000d: 7f # i32
000000e: 01 # num results
000000f: 7c # f64
0000009: 06 # FIXUP section size
# section "Function" (3)
0000010: 03 # section code
0000011: 00 # section size (guess)
0000012: 01 # num functions
0000013: 00 # function 0 signature index
0000011: 02 # FIXUP section size
# section "Table" (4)
0000014: 04 # section code
0000015: 00 # section size (guess)
0000016: 01 # num tables
# table 0
0000017: 70 # anyfunc
0000018: 00 # limits: flags
0000019: 00 # limits: initial
0000015: 04 # FIXUP section size
# section "Memory" (5)
000001a: 05 # section code
000001b: 00 # section size (guess)
000001c: 01 # num memories
# memory 0
000001d: 00 # limits: flags
000001e: 01 # limits: initial
000001b: 03 # FIXUP section size
# section "Export" (7)
000001f: 07 # section code
0000020: 00 # section size (guess)
0000021: 02 # num exports
0000022: 06 # string length
0000023: 6d65 6d6f 7279 # export name
0000029: 02 # export kind
000002a: 00 # export memory index
000002b: 08 # string length
000002c: 5f5a 3466 6163 7469 # export name
0000034: 00 # export kind
0000035: 00 # export func index
0000020: 15 # FIXUP section size
# section "Code" (10)
0000036: 0a # section code
0000037: 00 # section size (guess)
0000038: 01 # num functions
# function body 0
0000039: 00 # func body size (guess)
000003a: 01 # local decl count
000003b: 02 # local type count
000003c: 7e # i64
000003d: 02 # block
000003e: 40 # void
000003f: 20 # get_local
0000040: 00 # local index
0000041: 41 # i32.const
0000042: 01 # i32 literal
0000043: 48 # i32.lt_s
0000044: 0d # br_if
0000045: 00 # break depth
0000046: 20 # get_local
0000047: 00 # local index
0000048: ac # i64.extend_s/i32
0000049: 42 # i64.const
000004a: 01 # i64 literal
000004b: 7c # i64.add
000004c: 21 # set_local
000004d: 01 # local index
000004e: 42 # i64.const
000004f: 01 # i64 literal
0000050: 21 # set_local
0000051: 02 # local index
0000052: 03 # loop
0000053: 40 # void
0000054: 20 # get_local
0000055: 02 # local index
0000056: 20 # get_local
0000057: 01 # local index
0000058: 42 # i64.const
0000059: 7f # i64 literal
000005a: 7c # i64.add
000005b: 22 # tee_local
000005c: 01 # local index
000005d: 7e # i64.mul
000005e: 21 # set_local
000005f: 02 # local index
0000060: 20 # get_local
0000061: 01 # local index
0000062: 42 # i64.const
0000063: 01 # i64 literal
0000064: 55 # i64.gt_s
0000065: 0d # br_if
0000066: 00 # break depth
0000067: 0b # end
0000068: 20 # get_local
0000069: 02 # local index
000006a: b9 # f64.convert_s/i64
000006b: 0f # return
000006c: 0b # end
000006d: 44 # f64.const
000006e: 0000 0000 0000 f03f # f64 literal
0000076: 0b # end
0000039: 3d # FIXUP func body size
0000037: 3f # FIXUP section size
format binaire
format texte
(s-expression)
format binaire avec commentaires
WebAssembly, le langage.
(module)
(module
(func $getValue)
)
(module
(func $getValue (result i32)
i32.const 38)
)
exemple minimal
(module
(func $getValue (result i32)
i32.const 38)
)
0000000: 0061 736d # WASM_BINARY_MAGIC
0000004: 0100 0000 # WASM_BINARY_VERSION
# section "Type" (1)
0000008: 01 # section code
0000009: 05 # section size
000000a: 01 # num types
# type 0
000000b: 60 # func
000000c: 00 # num params
000000d: 01 # num results
000000e: 7f # i32
# section "Function" (3)
000000f: 03 # section code
0000010: 02 # section size
0000011: 01 # num functions
0000012: 00 # function 0 signature index
# section "Code" (10)
0000013: 0a # section code
0000014: 06 # section size
0000015: 01 # num functions
# function body 0
0000016: 04 # func body size
0000017: 00 # local decl count
0000018: 41 # i32.const
0000019: 26 # i32 literal
000001a: 0b # end
version texte func.wat
version binaire
func.wasm
exemple minimal
(module
(func $getValue (result i32)
i32.const 38)
)
(module
(func $getValue (result i32)
i32.const 38)
(export "getValue" (func $getValue))
)
exemple minimal
(module
(func $getValue (result i32)
i32.const 38)
(export "getValue" (func $getValue))
)
const binary = await fetch('./func.wasm')
38
func.wat
func.js
console
const binary = await fetch('./func.wasm')
const module = await WebAssembly.compile(binary)
const binary = await fetch('./func.wasm')
const module = await WebAssembly.compile(binary)
const instance = await WebAssembly.instantiate(module)
const binary = await fetch('./func.wasm')
const module = await WebAssembly.compile(binary)
const instance = await WebAssembly.instantiate(module)
console.log(instance.exports.getValue())
exemple minimal
func.wasm
(module
(func $add (param $a i32) (param $b i32) (result i32)
get_local $a
get_local $b
i32.add)
(export "add" (func $add))
)
const binary = await fetch('./add.wasm')
const module = await WebAssembly.compile(binary)
const instance = await WebAssembly.instantiate(module)
console.log(instance.exports.add(38, 4))
42
add.wat
add.js
console
avec paramètres
(module
(import "global" "log"
(func $log (param i32))
)
(func $mylog (param $value i32)
(call $log (get_local $value))
)
(export "logInteger" (func $mylog))
)
const imports = {
global: {
log: console.log
}
}
const instance = await WebAssembly.instantiate(
module, imports
)
instance.exports.logInteger(33)
33
console
appel d'une fonction JavaScript depuis WebAssembly
Mémoire de WebAssembly
l'instance lit et écrit dans sa mémoire
instance d'un module WebAssembly
zone mémoire de l'instance
instance d'un module WebAssembly
zone mémoire de l'instance
Mémoire de WebAssembly
JavaScript
mais il est également possible que JavaScript accède à cette mémoire
Mémoire de WebAssembly
la mémoire de l'instance
est vue
comme un BufferArray
par JavaScript
le partage se fait par import ou par export
comme pour les fonctions
(module
(func $set (param $index i32) (param $value i32)
(i32.store8
(get_local $index)
(get_local $value)
)
)
(export "set" (func $set))
(memory (export "memory") 1)
)
// ...
const buffer = instance.exports.memory.buffer
Int8Array [ 1, 4, 7 ]
Int8Array [ 1, 8, 7 ]
console
(module
(func $set (param $index i32) (param $value i32)
(i32.store8
(get_local $index)
(get_local $value)
)
)
(export "set" (func $set))
)
// ...
const buffer = instance.exports.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([1, 4, 7])
console.log(array)
instance.exports.set(1, 8)
console.log(array)
// ...
const buffer = instance.exports.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([1, 4, 7])
// ...
const buffer = instance.exports.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([1, 4, 7])
instance.exports.set(1, 8)
échanges mémoire entre JavaScript et WebAssembly
(module
(import "global" "memory" (memory 1))
(func $increment (param $index i32)
(i32.store8
(get_local $index)
(i32.add
(i32.load8_s (get_local $index))
(i32.const 1)
)
)
)
(export "increment" (func $increment))
)
const imports = {
global: {
memory: new WebAssembly.Memory({ initial: 1 })
}
}
Int8Array [ 6, 3, 4 ]
Int8Array [ 6, 3, 5 ]
console
const imports = {
global: {
memory: new WebAssembly.Memory({ initial: 1 })
}
}
const buffer = imports.global.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([6, 3, 4])
const imports = {
global: {
memory: new WebAssembly.Memory({ initial: 1 })
}
}
const buffer = imports.global.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([6, 3, 4])
console.log(array)
const instance = await WebAssembly.instantiate(
module, imports
)
instance.exports.increment(2)
console.log(array)
const imports = {
global: {
memory: new WebAssembly.Memory({ initial: 1 })
}
}
const buffer = imports.global.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([6, 3, 4])
const instance = await WebAssembly.instantiate(
module, imports
)
const imports = {
global: {
memory: new WebAssembly.Memory({ initial: 1 })
}
}
const buffer = imports.global.memory.buffer
const array = new Int8Array(buffer, 0, 3)
array.set([6, 3, 4])
const instance = await WebAssembly.instantiate(
module, imports
)
instance.exports.increment(2)
échanges mémoire entre JavaScript et WebAssembly - alternative
moteur WebAssembly
module
instance
imports et exports
mémoire linéaire
format binaire
Notions
int add(int a, int b) {
return a + b;
}
const imports = {
env: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({initial: 256}),
table: new WebAssembly.Table(
{initial: 0, element: 'anyfunc'}
)
}
}
const instance = await WebAssembly.instantiate(
module, imports
)
console.log(instance.exports._add(25, 4))
29
console
$ emcc add.c \
-Os -s SIDE_MODULE=1 \
-o add.wasm
add.c
compilation avec emscripten
emscripten et intégration avec JavaScript
#include <stdio.h>
int main() {
FILE *f = fopen("output.txt", "w");
fprintf(f, "WebAssembly");
fclose(f);
}
$ firefox webapp.html

>> Module.FS.readFile('output.txt', {encoding: 'utf8'})
"WebAssembly"
$ emcc main.c \
-s NO_EXIT_RUNTIME=1 \
-s 'EXTRA_EXPORTED_RUNTIME_METHODS=["FS"]' \
-o webapp.html
pseudo système de fichiers dans le navigateur fourni par emscripten
"WebAssembly"
$ wget http://zlib.net/zlib-1.2.11.tar.gz
$ tar xf zlib-1.2.11.tar.gz
$ cd zlib-1.2.11.tar.gz
$ emconfigure ./configure --prefix=./dist
$ emmake make
$ emmake make install
Emscripten pour compiler des outils existants
WebAssembly, pour faire quoi ?
jeux
édition de vidéo ou d'image
application musicale
reconnaissance d'image
VR et AR
visualisation et simulation scientifique
etc.
je prends
je prends
Exemples concrets
d'utilisation de WebAssembly


use case
Magellium - CNES
un outil de visualisation d'images satellite
avec traitements
100% côté navigateur
dans ces outils j'ai manipulé
des "petites images" (moins de 100 Mo)
dans des formats standards (PNG...)
lisibles par les navigateurs
en général
les données satellites
sont dans d'autres formats
JPEG 2000
très utilisé pour les images satellites
images de très grande taille
format de compression multiéchelle
encodages exotiques
(plus que trois "couleurs", pas de 8bits)
source : wikipedia
$ ll IMG_R2C1.JP2 -rw-r--r-- 1 ada ada 3.3G Nov 12 2009 IMG_R2C1.JP2 $ gdalinfo IMG_R2C1.JP2 Driver: JP2OpenJPEG/JPEG-2000 driver based on OpenJPEG library Files: IMG_R2C1.JP2 Size is 57344, 28672 Band 1 Block=4096x4096 Type=UInt16, ColorInterp=Red Overviews: 28672x14336, 14336x7168, 7168x3584, 3584x1792, 1792x896 NBITS=12 Band 2 Block=4096x4096 Type=UInt16, ColorInterp=Green Overviews: 28672x14336, 14336x7168, 7168x3584, 3584x1792, 1792x896 NBITS=12 Band 3 Block=4096x4096 Type=UInt16, ColorInterp=Blue Overviews: 28672x14336, 14336x7168, 7168x3584, 3584x1792, 1792x896 NBITS=12 Band 4 Block=4096x4096 Type=UInt16, ColorInterp=Undefined Overviews: 28672x14336, 14336x7168, 7168x3584, 3584x1792, 1792x896 NBITS=12
mais JPEG 2000 n'est pas un format lisible par les navigateurs
il existe des bibliothèques de lecture en C
OpenJPEG
open source
Kakadu
payant (licence de développement)
la référence pour le JPEG 2000
on utilise emscripten pour compiler Kakadu en WebAssembly
$ kdu_expand \
-i full_image.jp2 \
-o extract.bmp \
-region {0.3,0.05},{0.8,0.02}
Ligne de commande pour
extraire une partie d'une image JPEG 2000 et pour l'enregistrer en BMP


affichage dans le navigateur ?

bibliothèque JavaScript
affichage de
données cartographiques
et d'images


map.addLayer(new ol.layer.Tile({
source: new ol.source.TileImage({
tileLoadFunction: function (tile, zxyTileUrl) {
const [ x, y, w, h, reduce ] = getKduRegion(zxyTileUrl)
callWasmKduExpand([
'-i', inputJp2File,
'-o', subBmpFile,
'-region', `{${x},${y}},{${w},${h}})`,
'-reduce', `${reduce}`
])
const binary = readFileFromEmscriptenFS(subBmpFile)
const base64String = binaryArrayToBase64String(binary)
const dataUrl = 'data:image/bmp;base64,' + base64String
tile.getImage().src = dataUrl
}
})
}))
ol = OpenLayers
affichage ok et fluide !
(démo dans quelques slides)
mais...
uniquement sur des petites images JP2
(max 100 Mo)
raison :
fichiers stockés dans la mémoire du navigateur
(MEMFS d'emscripten)
comment lire et afficher
des images de plusieurs Go ?
utiliser l'API File de JavaScript
permet d'accéder à un intervalle d'octets si appelé depuis un Web Worker !
emscripten fournit
le pseudo système de fichiers WORKERFS
qui permet cela

wasm
archi pour fichier local
affichage d'un fichier local de
plusieurs Go
ok et fluide !
✔
affichage d'un fichier distant de
plusieurs Go ?


archi pour fichier local
archi pour fichier distant
wasm
wasm
affichage d'un fichier distant de
plusieurs Go
ok et fluide !
✔
WebAssembly
en dehors du navigateur
#include <stdio.h>
int main() {
printf("WebAssembly\n");
}
$ emcc main.c -o cli.js
$ ls
cli.js cli.wasm main.c
$ node cli.js
WebAssembly

détection d'avions avec l'algorithme ICF
traitements en batch de la
détection d'avions avec ICF
sur un ensemble de grandes images
avec le même binaire WebAssembly
mais côté serveur



Google Cloud
Functions
Google Cloud
PubSUb

old
exécution de wasm avec Node.js
moteur JavaScript
+
moteur WebAssembly
?
moteurs wasm
en dehors du navigateur


https://www.fastly.com/blog/announcing-lucet-fastly-native-webassembly-compiler-runtime

#include <stdio.h>
int main() {
printf("WebAssembly\n");
}
$ emcc main.c -o cli.js
$ ls
cli.js cli.wasm main.c
$ node cli.js
WebAssembly
$ ls
cli.js cli.wasm main.c
$ wasmer run cli.wasm
WebAssembly

WASI
WebAssembly system interface

https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
crédit : @linclark article mozilla hacks sur WASI

crédit : @linclark article mozilla hacks sur WASI

capability-based security
Rust + WebAssembly
Outils
Binding entre JavaScript et Rust
via WebAssembly
wasm-bindgen
Rust + WebAssembly
Outils
Binding entre JavaScript et Rust
via WebAssembly
wasm-bindgen
présentation par Nick Fitzgerald @fitzgen :
Rust + WebAssembly
Outils
"building, testing, and publishing Rust-generated WebAssembly"
wasm-pack
Rust + WebAssembly
Outils
pour une intégration
dans le workflow de build
plugin wasm pour webpack
Rust + WebAssembly
Comment ?
un Rust book de parcours de dev pas à pas
Tutoriel Game of Life


Merci !
Nicolas Decoster @ogadaki
A WebAssembly tale
By Nicolas Decoster
A WebAssembly tale
Conférence WebAssembly pour Electronic Tales
- 226