This is a WIP list of my notes on my research about "why my rust program is slow in wasm ?"
https://rustwasm.github.io/book/game-of-life/implementing.html#interfacing-rust-and-javascript
TODO :
https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/
https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/
How Wasm works. Interesting but not that relevant
"It’s likely that anybody who’s developing a WebAssembly module to be used by web developers is going to create a wrapper around that module. That way, you as a consumer of the module don’t need to know about memory management."
- wasm file are smaller because it is not plain text : they are downloaded faster, decoded faster and executed faster
- JS needs to parse, compile and optimize.
- When JS is optimized, it is faster.
This is conter intuitive because it implies that we have to write in wasm the glue code, not the main parts of the code because the main parts will be JITed anyway.
Lost source
- The WASM Machine is a stack based virtual machine.
- WebAssembly has been designed to solve the problem asm.js addresses.
- Easy validation of bytecode (Wasm : 1 page, Java : 150 pages)
- (Maybe from another source) : Bytecode can be loaded and used while it is downloaded
- Each module is executed in its own linear memory (an untrusted module can't do anything bad)
- "WebAssembly is on average 33.7% faster than asm.js, with validation taking only 3% of the time it does for asm.j.s"
- asm.js is basically Javascript wrote in a way that the interpreter can optimize aggressively while still respecting the standard
- This article also presents the key concept of wasm
- Benchmarks on part with asm.js (up to x2 compared to native code) but article written in 2017
https://medium.com/samsung-internet-dev/performance-testing-web-assembly-vs-javascript-e07506fd5875
Matrices multiplication with blocking
- WebAssembly is faster except on small matrices
- C is faster on the non blocked implementation (better optimization than the blocks)
- JS is faster with blocking
JIT might not be that powerful
https://blog.soshace.com/introduction-to-webassembly-the-magic-of-native-code-in-web-apps/
-
"WebAssembly also offers predictable performance — the ability to reduce the difference in browser performance (in some cases from 400% to less than 50%)."
-
It’s tempting to compare these technologies only by performance numbers, so here’s another curious finding: JavaScript and WebAssembly have equal peak performance. Still, preferring one technology over the other simply because of performance gains (if any) is short-sighted — there are a plethora of other factors that come into play.
But :
- We should also note that the performance equality of JavaScript and WebAssembly will probably disappear in the future: WebAssembly will soon receive support for threads and simd (single instruction, multiple data which will allow it to outperform JavaScript.
https://developers.google.com/web/updates/2019/02/hotpath-with-wasm
Benchmark the rotation of an image in wasm C, wasm Rust, wasm TypeScript and vanilla Javascript.
Time performances are roughly the same
Mimics a C / C++ kernel in Wasm.
- 0.8 to 3.5 slowdown (avg ~1.5) comparing to native performances
- The 300x speedup sometimes announced is wrong because "micro benchmarks" are not representative
- WebAssembly VM are new are just MVP (Minimal Viable Product), JS is 20 years old
https://engineering.widen.com/blog/A-Tale-of-Performance---Javascript,-Rust,-and-WebAssembly/
Implements a date compare function in wasm. Slower because text decoding is slow.
modern JavaScript engines like SpiderMonkey or Rhino follow the same approach, but what makes V8 stand out is that it does not produce any intermediate code. Someone, someday
~~ rustwasm/wasm-pack#558 ~~
- NodeJs has an option to disable optimizations (
--no optimization
)
https://youtu.be/uMuYaES4W3o?t=1220
- Heavy maths : expect 10% speedup
- UInt8Buffer copy may be slow, especially on Chrome / Node
- Use
benchmark.js
-
"FWIW the profilers showed that very little time was spent in wasm itself, so at least that part is fast here!"
-
How to profile JS / JS Glue Code / Wasm ?
-
Objects that are exported have a free function that should be manually called to let the glue code know that this object will not be used anymore
-
We don't actually own
JsValue
.JsValue
is worst than&JsValue
because JsValue's lifetime is more than this function call. In other words, an imported function that receives a JsValue doesn't really own the JsValue in the sense that if it loses the object, the object isn't destroyed. -
Every quad we create and exports must be explicitely freed which is not rdf.js.org friendly. This is reflected in the functiosn that returns strings that wasmbindgen generate in which
wasm.__wbindgen_free(r0, r1);
is always called at the end
-
"most notably enabling link time optimizations and manual panic handling"
-
Some people recommanded this options
[profile.release]
codegen-units = 1
lto = true
opt-level = 'z'
panic = 'abort'
List of compile option of rustc. lto = fat
may be interesting (but not that much because of thin lto.
Actual projects may help to find ideas about why our code is slow
- I think I didn't try this and that's stupid
-
wasmtime A Web Assembly interpreter
-
https://webassembly.studio/ Online Rust / Web Assembly compiler. Lacks wasm_bindgen so we can't use it to do quick experiments
wasm_bindgen reads the environement variable WASM_INTERFACE_TYPES
(set to 1) to generates Wasm Interfaces Types. ( rustwasm/wasm-bindgen#1725 )
But Interfaces Types are not yet merged into the Web Assembly standard
Found snippet :
pub fn cells(&self) -> *const Cell {
self.cells.as_ptr()
}
I needed to try
pub fn get_self(&self) -> *const Self {
self.as_ptr()
}
#[wasm_bindgen(js_class=$js_name)]
impl $rust_export_name {
/// Deletes the passed quad from this dataset
#[wasm_bindgen(js_name="delete")]
pub fn delete(&mut self, quad: JsImportQuad) -> Self {
let sophia_quad = SophiaExportDataFactory::from_quad(quad);
self.dataset.remove(
&sophia_quad._subject,
&sophia_quad._predicate,
&sophia_quad._object,
match &sophia_quad._graph {
None => None,
Some(x) => Some(x)
}
).unwrap();
self
}
}
error: cannot return a borrowed ref with #[wasm_bindgen]
error: aborting due to previous error
error: could not compile `sophia-wasm`.