You do: ArrayBuffer works great.
I have a lot of love for JS and there are many domains it does well in but unless you show numbers that prove otherwise, I’m quite sure you are totally wrong here.
I speak from experience :) Here's a demo of Javascript doing what it's unsuited for: buttery-smooth 60FPS on iOS https://twitter.com/theshawwn/status/1222280670908600321
Voice recognition: https://twitter.com/theshawwn/status/1151613837264732167
It's fair to say that writing a matrix multiply in pure JS might not be the best way to deploy your production code. But curing that is a matter of "run profiler, see clear hotspot, optimize hotspot." Especially now that you have so many options for dropping down to a lower-level stack.
JS is merely the wrapper, in many cases.
My point is that if you are using JS as a wrapper for a big black box of gpu computations that are close to the metal then you are not really using JS in any meaningful sense and can wrap anything else that has a much better library ecosystem and performance qualities for anything that you’re not just getting the gpu to crank out in a server context (which is implied by nodejs).
There’s the speed of generating inputs, the speed of transforming and passing data at the input/output boundaries, and the ability to conveniently and performantly work with data in memory natively (i.e. while not outsourcing the computations elsewhere) that matters here. Is JS a great choice for any of these things? Most importantly, the last thing? This library isn’t using ArrayBuffers for setting up all data or working on the data in JS so even if they work great for performance it seems totally irrelevant and let’s be honest, if it were working with ArrayBuffers directly you would be so far away from usual JS and any convenience JS offers, you might as well not be writing JS.
Or you might as well do, since it makes interop generally easy. In the library space I no longer have to worry where my web code begins, where my mobile code ends or where my backend code sits. All libraries can be used anywhere I feel necessary with zero interop pain.
The same arugment was applied to writing C in the late 70s (vs writing several types of assemblies for various types of hardware at the time)
I remember some years ago everyone was going nuts over how WebGL was able to render a detailed model in the browser, native games are dead! Except it was a single non-animated 3D model, in a simple or non-existent background. real games had many animated models, in complex environments, with complex interaction, physics, AI, audio and gameplay logic.
Ever since that comparison, I think, yeah, those demos look cool and indeed silky smooth, but most are a single simple thing. Real workloads tend to be much more complex: how does it fare with those?
(Of course if the vast majority of the processing time is in the GPU, then it really doesn’t matter what language, fast or slow, you use to control or feed it.)
As ever, browser support matters. Chrome and Firefox: yes. Safari? No.
At the moment I generate additional canvas elements in the code (in a pool, for reuse) and use them without adding them to the DOM. I'm happy with the resulting frame rates, and the flexibility the system gives me for creating responsive, accessible canvas elements in the web page. This is all done using a simple 2d context (even the bendy images code[2] is 2d, rendered in the main thread). I've avoided WebGL and the whole GPU acceleration thing because shaders scare me more than quaternions!
[1] - Scrawl-canvas - https://scrawl.rikweb.org.uk/
[2] - Bendy images interactive demo - https://scrawl.rikweb.org.uk/demo/canvas-024.html
There are a lot of upsides to running it through the web platform (the big one is that I can give people a link and they can click it, rather than convince them to run an app, but there are others, too).
There are downsides: performance is heavily variable. A new Chrome can launch and my performance can be 5x worse than before, which is why I now always test in Canary to make sure I'm ahead of all issues. In Chrome, the WebGL layer is pretty solid, but I still find plenty of bugs and performance issues (e.g. looking at a profile unearthed this very simple issue causing browser resizing to be way slower than it needed to be https://bugs.chromium.org/p/chromium/issues/detail?id=110347... ).
Firefox is quite a bit slower and has a bit more draw call overhead than Chrome (I'm guessing they run validation on the main thread rather than an IPC process, causing JS to block)
Most of the performance problems I encounter are actually JS-related, it's super easy to be knocked off of a fast path by complete accident, and there aren't good tools to know what I'm doing wrong. Math and ALU is plenty fast -- doing matrix multiplication on the CPU has never been a bottleneck, and you actually want that more on the CPU so that the GPU doesn't have to run a matrix mul for every pixel. Uniform data computation belongs on the CPU.
My big thing that I fight is the garbage collector, and you can see the awkward "manual register allocation" code style I sometimes resort to to make sure my allocations per-frame are super minimal, especially in fast math code. It doesn't look like traditional ergonomic JS code: https://github.com/magcius/noclip.website/blob/master/src/Su...
I could go on if wanted but this comment is already getting pretty long.
Basically you are saying code performance is nondeterministic across your user base and across time too?
I am glad WebGL is a thing for pages that need some 3D widgets where performance is not a big deal. But for general software or games? I will keep my native software.
So in the case of matrix multiplication, it knows that it's a double-vector of ints and runs the whole thing compiled. So it should, theoretically, be just as fast as a compiled language.
If those stats prove that it is always an array of floats, then machine code gets generated for an array of floats, with a trap handler for when the code fails.
The trap handler, if called, will throw away the code and restart the analysis.
So if you are nice for the compiler and keep your types regular, the machine code will not be thrown away and there are no speed bumps.
I still don't get it... How do you know when to activate the trap handler if you're not checking every time? It's not enough to wait for an error condition (e.g. an invalid pointer). If it's an array of integers, and I sneak in a string in there, what's stopping the JITed version from treating that pointer as an integer?