josdejong / mathjs

An extensive math library for JavaScript and Node.js
https://mathjs.org
Apache License 2.0
14.37k stars 1.24k forks source link

Accelerated computations with WASM #2644

Closed cshaa closed 2 years ago

cshaa commented 2 years ago

WebAssembly is a mature technology by now, which provides a significant performance boost to many web-based applications. Although a major undertaking, it might be beneficial to implement a WASM acceleration layer for math.js.

Original discussion in https://github.com/josdejong/mathjs/discussions/2212#discussioncomment-3339207: @m93a: Off-topic, but regarding WASM: don't you think it would be great to have some computation-intensive algorithms of math.js ported to WASM? If we used something like AssemblyScript for those, we could even provide two identical builds – one for modern environments with WASM and another in pure JS for legacy/restricted environments. This could give math.js a massive performance boost.

@gwhitney: Yes, I totally agree that for mathjs and/or its better successor you envision to be a "serious" compute engine, it would be best to write e.g. the guts of matrix algorithms in something like AssemblyScript. On the other hand, there have been projects like the (outdated) https://github.com/waylonflinn/weblas, so it seems to me that there will be some BLAS implementation in the browser we can just leverage... I don't much fancy porting it myself.

@m93a: Regarding the GPU-accelerated BLAS library, I would rather wait for the WebGPU standard to settle first. Computations on the GPU sound like the next logical step after having a WASM acceleration layer, as only highly parallelizable code would benefit from being run on the GPU, while virtually all computationaly intensive code would benefit from being in WASM.

Related: #1825, #2045

kungfooman commented 2 years ago

The issue with AssemblyScript is that it still doesn't support variadic arguments and therefore also fails to replicate JS behaviour of every variadic function in JS. So by necessity you end up having to manage two incompatible versions, because the assumption that you can just use good old JS and turn it into TS for AS is wrong.

I've been there myself for another JS project, but eventually running into issues like this one: https://github.com/AssemblyScript/assemblyscript/issues/1501

(2 years later, not fixed)

And then JS JIT is extremely fast aswell, my AS version ended up being slower than pure JS JIT because of WASM-to-Javascript bridge performance loss.

So think twice before you uglify your TS code with all kinds of type annotations that AS requires and then it still doesn't give you what you hope for, but the introduced complexity is enormous (say hi to pointers and invalidated typed arrays after memory regrowth for example).

cshaa commented 2 years ago

Yes, that's definitely a very good remark. While I have first-hand experience with Rust compiled to WASM being orders of magnitude faster than the corresponding JS in my app, I am well aware that in many other scenarios the performance of WASM might be only marginally better, or even worse than pure JS. Deciding which codepaths to accelerate will require lots and lots of benchmarks.

I haven't used AssemblyScript yet, but I've noticed that it's still pretty barebones and its interop with TS is pretty shaky. However, being able to share code between WASM and non-WASM parts of the project would be really awesome and might still be worth the price of uglier / less expressive code. Remember, only the most expensive computations need to be done in WASM, so most of the library could be ordinary JS / TS.

That being said, many proofs-of-concept will have to be done before comitting to any specific solution, and it's possible that we'll find that AS is simply not worth the hassle and programming the accelerated functions in C / Rust / Zig / whatever is much better.

josdejong commented 2 years ago

Thanks. Yes WASM could be interesting

So think twice before you uglify your TS code with all kinds of type annotations that AS requires and then it still doesn't give you what you hope for, but the introduced complexity is enormous (say hi to pointers and invalidated typed arrays after memory regrowth for example).

I second that, we should first do a small proof of concept to figure out if there is much to gain and what the drawbacks are.

One approach could be to rewrite mathjs completely in WebAssembly. An other approach could be to have for example a new Matrix data type or numeric type written in WebAssembly, and import that into mathjs. Similarly, it could be interesting to look into typed arrays, see #43, or into utilizing WebGL for matrix computations.