Hacker News new | ask | show | jobs
by axefrog 2986 days ago
Does anyone have a good sense of the current performance impact of crossing the boundary between JS and WASM? I've often thought it'd be great to be able to expose high-performance data structures (and other infrastructure-level stuff) via WASM and then make use of them from JavaScript, but I seem to recall that the interop performance cost is currently too high to make it worth doing, which leaves WASM mainly only useful for long-running tasks, such as game engines, expensive graph layout algorithms and so forth.

Edit: https://hacks.mozilla.org/2018/04/javascript-to-rust-and-bac...

> "The wasm-bindgen code generation has been designed with the future host bindings proposal in mind from day 1. As soon as that’s a feature available in WebAssembly, we’ll be able to directly invoke imported functions without any of wasm-bindgen‘s JS shims. Furthermore, this will allow JS engines to aggressively optimize WebAssembly manipulating the DOM as invocations are well-typed and no longer need the argument validation checks that calls from JS need. At that point wasm-bindgen will not only make it easy to work with richer types like strings, but it will also provide best-in-class DOM manipulation performance."

4 comments

The cost is more than a JS->JS call, but not drastically so. Every argument must be converted from a number to an int32/float32/float64, which for SMI values is a single branch, for heap numbers a branch and a load. For other JS values, a ValueOf() operation on the JS value.

V8 generates little wrappers for these, with inline conversions. It does not currently inline the little wrapper functions, nor use ICs for the conversions inside, since branches are generally enough to get the interesting fast cases.

The idea of a super-expensive call is therefore a bit of a myth. You can try to measure the cost yourself, but be careful! Most microbenchmarks will end up comparing the difference between an inlined JS call (as low as zero overhead) versus a non-inlined WASM->JS or JS->WASM call. The proper comparison would be instead with a non-inlined JS->JS call. Defeating inlining for JS->JS calls is tricky. You can do it with cross-realm calls, or by trying manipulating polymorphism. In either case, it's pretty tricky, so good luck.

If it's nearly impossible to defeat inlining, wouldn't the appropriate comparison be against inlined JS?
You can defeat inlining by using closures:

    function thunk(x){ return function() { x() } }
    const thunkFoo = thunk(foo)
    const thunkBar = thunk(bar)

    for(var i = 0; i < 10000; i++) {
      thunkFoo()
      thunkBar()
    }
This is why the "Maybe you don't need Rust to speed up your JS" author was creating functions dynamically using `new Function()`.

Maybe you cal get away with

    function call(x){ x() }
    for(var i = 0; i < 10000; i++) {
      call(foo)
      call(bar)
    }

I didn't test the latter though, whereas the former is empirically slower if you call more than one thunk in your benckmark loop (a single thunk will have the call inlined).

https://mrale.ph/blog/2018/02/03/maybe-you-dont-need-rust-to...

For v8, doesn’t a try/catch block defeat inlining? It used to, but that was a long time ago.
No, not with TurboFan (since Q2 2017).
It depends how much you are expecting, for instance this WebGL demo does about 15k draw calls per frame on my 2014 MBP with integrated Intel GPU before it drops below 60Hz, that's about 1.8 million WebGL calls per second. On this machine, performance of a native executable is quite similar, so the WASM-to-JS overhead isn't big enough to be noticeable for those 1.8m calls/sec. I know, it's not a really useful benchmark, I guess what I want to say is that for most real-world problems the calling overhead shouldn't matter much, since other things will break down first.

PS: link to demo: http://floooh.github.io/oryol/asmjs/DrawCallPerf.html

I did a basic benchmark [0] when partially porting gl-matrix to a near 1:1 wasm equivalent.

There is some overhead but it seems acceptable. I get drops when calling a wasm function with a lot parameters (see .set).

[0] https://maierfelix.github.io/glmw/mat4/

Note: This benchmark is far from perfect but hopefully can provide some insights

I've read that cryptography algorithms written in wasm (wat format) are nearly as fast as their C implementations.
is there a format called "wat"?? lol
“Web assembly text” format, yes.
World of Warcraft's config files are all ".wtf" so there's that, too.