WebAssembly: un état des lieux

Le navigateur est une machine virtuelle

@m4d_z
alwaysdata logo

Petit rappel historique

  • 2001: Prototype, Scriptaculo·us, Mootools
  • 2005: jQuery
  • 2010: SPA / Backbone
  • 2012: Angular
  • 2015: virtual-dom, React, Vue.js
  • 2018: PWA !
  • 2019: Sinon, ça se passe bien, la RAM, chez vous ?

WebAssembly, c’est quoi ?

Emscripten

Performances, on y est presque

asm.js
(sous-ensemble JS optim)

JS : event-driven

Mono-threading

  • bloquer toute l’UI
  • avoir des fuites de memoire
  • nextTick : setTimeout({...}, 0)

Background :
WebWorker, ServiceWorkers

La course à la perf continue

WebAssembly, c’est un nouveau langage, proche d’un langage machine (un assembleur) qui permet d’avoir des performances quasi natives, dans le navigateur.

0200 20 00
0202 42 00
0204 51
0205 04 7e
0207 42 01
020A 05
020B 20 00
020D 20 00
0210 42 01
0211 7d
0212 10 00
0214 7e
0217 0b

C’est pas un langage !

get_local 0
i64.const 0
i64.eq
if i64
    i64.const 1
else
    get_local 0
    get_local 0
    i64.const 1
    i64.sub
    call 0
    i64.mul
end

Compiler pour le Web

LLVM (die, GCC)
pour gérer la chaîne de compilation

Pourquoi ?
parce que c’est possible !

JS ne peut pas tout,
on a parfois besoin de partager
de la codebase hétérogène

Le navigateur est une Virtual Machine

  • PWA
  • Electron
  • Cordova
  • etc

WASM

  • du code, dans n’importe quel langage
  • compilé via LLVM vers WASM
  • chargé via JS
  • exécuté dans le browser

Back to basics: C’est pas si simple

Stack Based VM

Stack based vs. Register based

  • Register proche CPU, plus performant
  • Stack plus flexible, plus léger
  • JVM, CLR &… JS JIT : stack based VM

Stack based VM

Magic!

La stack est le point central

Opération : (1 + 2 * 3) / 7
instructions: 1.2.3.MUL.ADD.7.DIV

Assembleur !

PUSH 1
PUSH 2
PUSH 3
MUL
ADD
PUSH 7
DIV

Moteur JS logique

  • SpiderMonkey
  • V8
  • Chakra
  • JavaScriptCore (JSC)

Au départ :

  • JS VM engine
  • Interpreteur
  • Compilateur JIT

Le compilateur JIT
produit un code machine
rapide mais limité

WASM utilise le même moteur

  • échanges JS
  • unboxing
  • appels monomorphiques
  • les methodes built-ins

Moteur d’exécution capable de
manipuler tous les langages logiques,
pour le Web !

Rust

Pourquoi Rust ? Parce que :

  • C++, c’est mignon, mais merci, j’ai qu’un cerveau
  • supprime les data-race par design
  • fortement typé
  • iterators !
  • excellent controle de la memoire
  • communauté
  • toolchain

Hello World

fn main() {
    // Print text to the console
    println!("Hello World!");
}

Modules By Design

mod sound {
    pub mod instrument {
        pub fn pouet() {
            // ...
        }
    }
}
fn main() {
    crate::sound::instrument::pouet();
}

Typing & iterators

mod squares {
    pub fn sum_from_zero( n: i32) -> i32 {
        (0 ..= n).fold(0, |a, b| a + b)
    }
}

Cargo

$ cargo build
   Compiling hello_world v0.1.0 (file:///path/to/package/hello_world)
$ cargo run
     Fresh hello_world v0.1.0 (file:///path/to/package/hello_world)
   Running `target/hello_world`
Hello, world!

Ready to production ‽

Les 4 Fantastiques !

Open Stardard

Un code en WASM Rust pour le Web

pub fn greet(name: &str) {
    alert(&format!("Hey! {}!", name));
}

Comment ?

  1. Fetch
  2. Load / Compile (2×)
  3. Instantiate
  4. Access fn

Fetch !

fetch('my-module.wasm')
  .then(res => res.arrayBuffer())
  .then(instance => WebAssembly.instantiate(instance))
  .then(module => {
    // module.instance.exports[...]
  })

Passer des valeurs à WASM

...
.then(module => {
  const sum = module.instance.exports.add(7, 4)
  console.log(sum)
})

4 types

  • 2 entiers
  • 2 flottants

Problème :
je manipule pas que des nombres

Un nombre, c’est…
un pointeur mémoire !

Linear Memory

const pointer = 0

const LM = new WebAssembly.Memory({ initial: 1 })
const sharedMem = new Uint8Array(LM.buffer)

const str = "Hello World"

[].forEach.call(
  btoa(str),
  (char, idx) => sharedMem[pointer + idx] = char.charCodeAt(0)
)
// Uint8Array(65536) [ 83, 71, 86, 115, 98, 71, 56, 103, 86, 50, … ]

wasm-bindgen!

wasm-bindgen facilitates high-level interactions between wasm modules and JavaScript.

extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hey! {}!", name));
}
const wasm = import("./js_hello_world")
wasm.then(wasm => wasm.greet("Hello World")
  • export des fn JS dans Rust
  • js
    • js-types
    • Boxes
    • catch
    • getter / setter
    • namespaces
  • web-sys
    • DOM
    • Fetch
    • Canvas / requestAnimationFrame
    • WebAudio / WebGL

Distribuer ? wasm-pack !

📦✨ your favorite rust → wasm workflow tool!

WASM : un état des lieux

Pour le moment :

  • compile
  • rapide
  • léger
  • linear memory via TypedArray

Next (entre-autre) :

  • Multi-threading
  • Streaming compilation / Tiered Compiler
  • JS Modules exchange / Garbage Collector
  • Portability / Runtime / IoT

Know The Web!

  • HTML/DOM → interfaces
  • CSS → styling
  • JS → exécution logique UI
  • WASM → background operations
m4dz's avatar
m4dz

Paranoïd Web Dino · Tech Evangelist

alwaysdata logo
https://www.alwaysdata.com

Questions?

Thank You!


Available under licence CC BY-SA 4.0

Illustrations

Wikipedia Commons

Interleaf images

Courtesy of Unsplash and Pexels contributors

Icons

All icons are from The Noun Project

Fonts

  • Cover Title: Sinzano
  • Titles: Argentoratum
  • Body: Mohave
  • Code: Fire Code

Tools

Powered by Reveal.js

Source code available at
https://git.madslab.net/talks