Rust et WebAssembly

Rust Toulouse

Nicolas Decoster

@ogadaki

Vue.js

WebAssembly ?

Web + Assembleur ?

?!?!?

"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!

?

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, NaCL...

+ 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 le développeur

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

(en cours)

j'ai un code en Go

je veux le compiler en WebAssembly

Comment ?

natif avec le compilateur

(en cours)

j'ai un code en Java

je veux le compiler en WebAssembly

Comment ?

Tu attends...

j'ai un code en Python

j'ai un code en etc.

#include <stdio.h>
int main() {
    printf("WebAssembly\n");
}
$ node cli.js
WebAssembly
$ emcc main.c -s WASM=1 -o cli.js
#include <stdio.h>
int main() {
    printf("WebAssembly\n");
}
$ firefox webapp.html
$ emcc main.c -s WASM=1 -o webapp.html

   WebAssembly

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 $apply (param $value i32)
    (call $log (get_local $value))
  )
  (export "apply" (func $apply))
)
const imports = {
  global: {
    log: console.log
  }
}
const instance = await WebAssembly.instantiate(
  module, imports
)

instance.exports.apply(33)
33

console

appel d'une fonction JavaScript depuis WebAssembly

Mémoire linéaire 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 linéaire de WebAssembly

JavaScript

mais il est également possible que JavaScript accède à cette mémoire

Mémoire linéaire 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

$ 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

Prototype

Magellium - CNES

un outil de visualisation d'images satellite

avec traitements

100% côté navigateur

?

éditeur de texte

collaboratif

offline ready

mon use case

possibilité d'éditer même en déconnecté

resynchronisation à la reconnexion

sans centralisation

cohérence du résultat

entre tous les éditeurs connectés

mon use case

Operational transformation (OT) ?

Conflict-free replicated data type (CRDT) !

arbre causal !!

"Data Laced with History:

Causal Trees & Operational CRDTs"

Alexei Baboulevitch

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

Rust + WebAssembly

Comment ?

je veux mixer

rust + wasm + webpack + Vue.js

pour mon use case

Rust + WebAssembly

Comment ?

npm init rust-webpack my_project

pour mon use case

installation de vue et des outils de build associés

npm run start

configuration de webpack et de babel

Merci !

Nicolas Decoster      @ogadaki

Rust Toulouse

Made with Slides.com