Web assembly
10 януари 2023
Административни неща
- Трето домашно приключи
Административни неща
- Трето домашно приключи
- Четвърто домашно няма да има
- Ще скалираме точките от 15 на 20 на домашно
Административни неща
- Трето домашно приключи
- Четвърто домашно няма да има
- Ще скалираме точките от 15 на 20 на домашно
- Изберете си проект
- предложете в #projects канала в дискорд или по имейл fmi@rust-lang.bg
- информация: https://fmi.rust-lang.bg/guides/projects
- showcase от минали години: https://github.com/fmi/rust-projects
Web assembly
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.
Източник: https://webassembly.org/
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
- алтернатива на JavaScript
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
- алтернатива на JavaScript
- по-малък и компактен
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
- алтернатива на JavaScript
- по-малък и компактен
- няма нужда от парсване и интерпретиране на сложен текстов формат, какъвто е един програмен език
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
- алтернатива на JavaScript
- по-малък и компактен
- няма нужда от парсване и интерпретиране на сложен текстов формат, какъвто е един програмен език
- по-бърз - използва операции от по-ниско ниво
Web assembly
- формат за bytecode, който се изпълнява от виртуална машина
- основна цел - изпълняване на client-side код в уеб сайтове
- алтернатива на JavaScript
- по-малък и компактен
- няма нужда от парсване и интерпретиране на сложен текстов формат, какъвто е един програмен език
- по-бърз - използва операции от по-ниско ниво
- може да се използва и извън уеб среда, като портативен формат за изпълним код
Поддръжка
- Кои езици поддържат компилация до web assembly?
- https://github.com/appcypher/awesome-wasm-langs
Компилация на rust до wasm
- обикновенно за компилиране до wasm се използва
wasm_bindgen
Компилация на rust до wasm
- обикновенно за компилиране до wasm се използва
wasm_bindgen
- но първо ще видим как се прави компилация на ръка
Компилация на rust до wasm
- обикновенно за компилиране до wasm се използва
wasm_bindgen
- но първо ще видим как се прави компилация на ръка
- използвайки само
cargo
иrustc
Компилация на rust до wasm
Крос компилация
- най-често от компилаторите се очаква да генерират код за същата платформа върху която се изпълняват
Компилация на rust до wasm
Крос компилация
- най-често от компилаторите се очаква да генерират код за същата платформа върху която се изпълняват
- но понякога искаме компилираната програма или библиотека да се изпълнява на друга платформа
Компилация на rust до wasm
Крос компилация
- най-често от компилаторите се очаква да генерират код за същата платформа върху която се изпълняват
- но понякога искаме компилираната програма или библиотека да се изпълнява на друга платформа
- например пишем код за микро контролер
Компилация на rust до wasm
Крос компилация
- най-често от компилаторите се очаква да генерират код за същата платформа върху която се изпълняват
- но понякога искаме компилираната програма или библиотека да се изпълнява на друга платформа
- например пишем код за микро контролер
- това се нарича cross compilation
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
- архитектурата на процесора (набор от процесорни инструкции)
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
- архитектурата на процесора (набор от процесорни инструкции)
- операционната система (ако има такава)
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
- архитектурата на процесора (набор от процесорни инструкции)
- операционната система (ако има такава)
- и понякога допълнително детайли за средата в която ще се изпълнява
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
- архитектурата на процесора (набор от процесорни инструкции)
- операционната система (ако има такава)
- и понякога допълнително детайли за средата в която ще се изпълнява
- примери
Компилация на rust до wasm
Крос компилация
- rustc използва llvm като backend за генериране на крайния код
- llvm поддържа задаване на платформата за която да генерира код чрез target tuple
- target tuple-а оказва
- архитектурата на процесора (набор от процесорни инструкции)
- операционната система (ако има такава)
- и понякога допълнително детайли за средата в която ще се изпълнява
- примери
- x86_64-unknown-linux-gnu
- x86_64-pc-windows-msvc
- aarch64-unknown-linux-gnu
Компилация на rust до wasm
Крос компилация
- web assembly се поддържа като вид "процесорна архитектура"
Компилация на rust до wasm
Крос компилация
- web assembly се поддържа като вид "процесорна архитектура"
wasm32-unknown-unknown
Компилация на rust до wasm
Крос компилация
- web assembly се поддържа като вид "процесорна архитектура"
wasm32-unknown-unknown
rustup target add wasm32-unknown-unknown
- това ще ни инсталира прекомпилирана версия на стандартната rust библиотека, към която ще се линква
Компилация на rust до wasm
Крос компилация
Има и още няколко web assembly target tuple-а:
Компилация на rust до wasm
Крос компилация
Има и още няколко web assembly target tuple-а:
- wasm е предназначен да се изпълнява в sandbox-ната среда, по подразбиане няма достъп до външни функционалности
Компилация на rust до wasm
Крос компилация
Има и още няколко web assembly target tuple-а:
- wasm е предназначен да се изпълнява в sandbox-ната среда, по подразбиане няма достъп до външни функционалности
wasm32-wasi
- WASI е стандартизиран интерфейс, който дава достъп до функции на ОС
Компилация на rust до wasm
Крос компилация
Има и още няколко web assembly target tuple-а:
- wasm е предназначен да се изпълнява в sandbox-ната среда, по подразбиане няма достъп до външни функционалности
wasm32-wasi
- WASI е стандартизиран интерфейс, който дава достъп до функции на ОСwasm32-unknown-emscripten
- позволява достъп до някои стандартни POSIX функции. Създадено е за използване от C/C++ и няма добра поддръжка сред Ръст екосистемата
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
- премахва се достъпа до стандартната библиотека (
std
)
- премахва се достъпа до стандартната библиотека (
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
- премахва се достъпа до стандартната библиотека (
std
) - има достъп само до
core
(иalloc
като външна библиотека)
- премахва се достъпа до стандартната библиотека (
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
- премахва се достъпа до стандартната библиотека (
std
) - има достъп само до
core
(иalloc
като външна библиотека) - използва се за embedded среди, където обикновенно няма ОС
- премахва се достъпа до стандартната библиотека (
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
- премахва се достъпа до стандартната библиотека (
std
) - има достъп само до
core
(иalloc
като външна библиотека) - използва се за embedded среди, където обикновенно няма ОС
- премахва се достъпа до стандартната библиотека (
- при wasm не е нужно ограничаване до no_std - може да се използва
std
библиотеката
Компилация на rust до wasm
std и no_std
- Rust поддържа глобален атрибут
#![no_std]
- премахва се достъпа до стандартната библиотека (
std
) - има достъп само до
core
(иalloc
като външна библиотека) - използва се за embedded среди, където обикновенно няма ОС
- премахва се достъпа до стандартната библиотека (
- при wasm не е нужно ограничаване до no_std - може да се използва
std
библиотеката - но ако се използва функция, която не се поддържа (напр. работа с нишки) програмата ще panic-не
Пример 1
Rust до Wasm
Cargo.toml
[lib]
crate-type = ["cdylib"]
Трябва проекта да се компилира като библиотека.
Тази библиотека ще се зареди от javascript код и ще се използват функциите дефинирани в нея.
Пример 1
Rust до Wasm
src/lib.rs
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
Функцията add
ще бъде достъпна от javascript
Пример 1
Rust до Wasm
cargo build --release --target wasm32-unknown-unknown
Това ще генерира target/wasm32-unknown-unknown/release/example_01.wasm
Пример 1
Изпълняване в уеб браузър
- нужен e статичен file server
Пример 1
Изпълняване в уеб браузър
- нужен e статичен file server
- .wasm не може да се импортира от html файл, поради CORS защити
Пример 1
Изпълняване в уеб браузър
- нужен e статичен file server
- .wasm не може да се импортира от html файл, поради CORS защити
- трябва да се използва javascript функцията
fetch
Пример 1
Изпълняване в уеб браузър
- нужен e статичен file server
- .wasm не може да се импортира от html файл, поради CORS защити
- трябва да се използва javascript функцията
fetch
- може да използвате basic-http-server:
cargo install basic-http-server
Пример 1
Изпълняване в уеб браузър
- нужен e статичен file server
- .wasm не може да се импортира от html файл, поради CORS защити
- трябва да се използва javascript функцията
fetch
- може да използвате basic-http-server:
cargo install basic-http-server
- ако имате python може да ползвате
python3 -m http.server
(обяснение)
Пример 1
Изпълняване в уеб браузър
Сървъра ще връща следните файлове:
- index.html
- index.js
- wasm_lib.wasm
cp target/wasm32-unknown-unknown/release/example_01.wasm \
wasm_lib.wasm
Пример 1
Изпълняване в уеб браузър
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello wasm!</title>
</head>
<body>
<script src="./index.js" type="module"></script>
</body>
</html>
Пример 1
Изпълняване в уеб браузър
index.js
WebAssembly.instantiateStreaming(fetch('./wasm_lib.wasm'), {})
.then(wasm => {
// през `instance.exports` можем да достъпваме функциите,
// които сме дефинирали в нашата библиотека.
console.log("add(1, 2) = ", wasm.instance.exports.add(1, 2))
})
Пример 1
Ограничения
- този пример работи, защото използвахме
i32
Пример 1
Ограничения
- този пример работи, защото използвахме
i32
- но по-сложни типове, като низове, масиви, обекти, не могат да се прехвърлят директно между js и wasm
Пример 1
Ограничения
- този пример работи, защото използвахме
i32
- но по-сложни типове, като низове, масиви, обекти, не могат да се прехвърлят директно между js и wasm
- нужен е специален код от двете страни на ffi границата
Пример 1
Ограничения
- този пример работи, защото използвахме
i32
- но по-сложни типове, като низове, масиви, обекти, не могат да се прехвърлят директно между js и wasm
- нужен е специален код от двете страни на ffi границата
- за това се използва
wasm-bindgen
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
- конвертиране на Rust-ки типове до JS еквиваленти - uint, string, vec, …
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
- конвертиране на Rust-ки типове до JS еквиваленти - uint, string, vec, …
- копиране на памет между wasm и JS
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
- конвертиране на Rust-ки типове до JS еквиваленти - uint, string, vec, …
- копиране на памет между wasm и JS
- четене на wasm паметта от JS
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
- конвертиране на Rust-ки типове до JS еквиваленти - uint, string, vec, …
- копиране на памет между wasm и JS
- четене на wasm паметта от JS
- конвертиране на низове между utf8 и utf16
Пример 1
Wasm-bindgen
- wasm-bindgen се грижи да направи интерфейс между wasm и javascript
- конвертиране на Rust-ки типове до JS еквиваленти - uint, string, vec, …
- копиране на памет между wasm и JS
- четене на wasm паметта от JS
- конвертиране на низове между utf8 и utf16
- и др.
Wasm bindgen
Необходима е програмата wasm-bindgen
, която може да се инсталира през cargo
cargo install wasm-bindgen-cli
Необхидима е и библиотеката wasm-bindgen
. Cargo.toml:
[dependencies]
wasm-bindgen = { version = "0.2.69", features = ["serde-serialize"] }
Wasm bindgen
Използване
cargo build --release --target wasm32-unknown-unknown
wasm-bindgen target/wasm32-unknown-unknown/release/wasm_lib.wasm \
--out-dir pkg \
--target web
- копира wasm файла в
pkg/wasm_lib_bg.wasm
- генерира js файл с wrapper-и на експортнатите функции
pkg/wasm_lib.js
- генерира и typescript дефиниции
Пример 2
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add_i32(a: i32, b: i32) -> i32 {
a + b
}
#[wasm_bindgen]
pub fn add_u32(a: u32, b: u32) -> u32 {
a + b
}
#[wasm_bindgen]
pub fn greet() -> String {
"hello wasm".to_string()
}
// `wasm_bindgen` атрибута позволява да подаваме структурата
// на JS код като opaque тип, т.е. указател
#[wasm_bindgen]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct User {
name: String,
age: u32,
}
#[wasm_bindgen]
impl User {
// В този случай `User` е opaque type
pub fn new(name: String, age: u32) -> User {
User { name, age }
}
pub fn to_js_value(&self) -> JsValue {
// Сериализира структурата до JSON и я подава на JS,
// където ще се десериализира до JS обект
JsValue::from_serde(self).unwrap()
}
}
Пример 2
import { add_i32, add_u32, greet, User, default as init } from './pkg/wasm_lib.js'
init('./pkg/wasm_lib_bg.wasm')
.then(_wasm => {
console.log(greet())
console.log("add_i32(-1, -2) = ", add_i32(-1, -2)) // -3
console.log("add_u32(-1, -2) = ", add_u32(-1, -2)) // 4294967293
const user = User.new("Иванчо", 10)
console.log(user) // Object { ptr: 1114144 }
console.log(user.to_js_value()) // Object { name: "Иванчо", age: 10 }
})
Импортиране на функции
Wasm модул има достъп единсвено до функции, които са му подадени експлицитно от страна на JS при създаването му.
Rust
extern "C" {
pub fn myStuff();
}
Javascript
function myStuff() { /* ... */ }
const importObject = {
env: { myStuff }
}
WebAssembly
.instantiateStreaming(fetch('./wasm_lib.wasm'), importObject)
.then(wasm => {
/// ...
})
Импортиране на функции
Или чрез wasm-bindgen
Rust
#[wasm_bindgen(module = "/exports.js")]
extern "C" {
pub fn myStuff();
}
Javascript
// exports.js
export function myStuff(msg) {
console.log("myStuff called");
}
Връзка с браузърни API-та
js-sys и web-sys
- https://docs.rs/js-sys
- bindings към стандартни javascript API-та
- https://docs.rs/web-sys/
- bindings към уеб и DOM API-та
- документации и примери в wasmbindgen книгата (https://rustwasm.github.io/wasm-bindgen/examples/dom.html)
Връзка с браузърни API-та
Можем да използваме браузъра като мощна "библиотека" за графичен интерфейс.
Canvas, бутони, форми, input полета, …
<body>
<canvas id="the_canvas_id"></canvas>
</body>
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id("the_canvas_id").expect("Can't find canvas");
// ...
Примери
Статична търсачка
Инструменти за wasm
wasm-pack
- https://rustwasm.github.io/wasm-pack/installer/
- или
cargo install wasm-pack
Инструменти за wasm
wasm-pack
- https://rustwasm.github.io/wasm-pack/installer/
- или
cargo install wasm-pack
- помага за интеграция с javascript света - npm, bundler-и, …
Инструменти за wasm
wasm-pack
- https://rustwasm.github.io/wasm-pack/installer/
- или
cargo install wasm-pack
- помага за интеграция с javascript света - npm, bundler-и, …
- автоматизира това, което правихме досега ръчно
Инструменти за wasm
wasm-pack
- https://rustwasm.github.io/wasm-pack/installer/
- или
cargo install wasm-pack
- помага за интеграция с javascript света - npm, bundler-и, …
- автоматизира това, което правихме досега ръчно
- инсталация на target
wasm32-unknown-unknown
сrustup
- компилация на rust до
.wasm
wasm-bindgen
за js binding-и- някои оптимизации
- инсталация на target
Оптимизации
Aко ще добавяме wasm към web страница обикновено размера на модула е по-важен критерий от скоростта му
Оптимизации
Aко ще добавяме wasm към web страница обикновено размера на модула е по-важен критерий от скоростта му
[profile.release]
opt-level = "z"
lto = "thin"
codegen-units = 1
Оптимизации
Aко ще добавяме wasm към web страница обикновено размера на модула е по-важен критерий от скоростта му
[profile.release]
opt-level = "z"
lto = "thin"
codegen-units = 1
- wasm-opt - премахване на ненужни символи
Оптимизации
Aко ще добавяме wasm към web страница обикновено размера на модула е по-важен критерий от скоростта му
[profile.release]
opt-level = "z"
lto = "thin"
codegen-units = 1
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
- сравнително прост bytecode формат
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
- сравнително прост bytecode формат
- поддръжка на компилация до wasm от много езици
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
- сравнително прост bytecode формат
- поддръжка на компилация до wasm от много езици
- преносим - може да се изпълнява на всякакви платформи
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
- сравнително прост bytecode формат
- поддръжка на компилация до wasm от много езици
- преносим - може да се изпълнява на всякакви платформи
- сигурен - всички инструкции и достъпи до памет се валидират
Wasm извън браузъра
Въпреки, че wasm е създаден с идеята да се използва за уеб, намира и други приложения.
Web assembly като формат притежава някои удобни свойства:
- сравнително прост bytecode формат
- поддръжка на компилация до wasm от много езици
- преносим - може да се изпълнява на всякакви платформи
- сигурен - всички инструкции и достъпи до памет се валидират
- sandbox-нат - няма достъп до външния свят
Wasm извън браузъра
Wasmtime
- runtime за web assembly и WASI
- като библиотека или като интерпретатор
- https://wasmtime.dev/
Примери
Vscode tree sitter plugin
- https://marketplace.visualstudio.com/items?itemName=evgeniypeshkov.syntax-highlighter
- tree sitter е генератор на инкрементални парсери за програмни езици
- tree sitter генерира C код по граматика
- plugin-а дистрибутира този C код, компилиран до wasm модул
Примери
Wasm-4
- https://wasm4.org/
- библиотека емулатор на минималистична конзола
- играта се компилира до wasm модул и се изпълнява от "конзолата"