Making JavaScript run fast on WebAssembly
bytecodealliance.org
bytecodealliance.org
I also did basically this exact same thing for Go [0] when I realized their initialization in WASM was very heavy[1]. Basically I ran up until the real Go main started which includes a ton of runtime package/data initialization, and took a snapshot of the data and baked it back into the WASM and removed all the pre-main code. Granted this was years ago so I don't know if it still works on generated code today, but the idea is the same.
I think languages compiling to WASM, if they can, should run their initialization code and snapshot the data. A lot of people don't realize the number of init instructions to bootstrap a runtime these days. Go alone has thousands of instructions just to initialize the unicode tables. Over 4 million instructions before main were removed from hello-world Go WASM init by pre-processing.
0 - https://github.com/cretz/go-wasm-bake 1 - https://github.com/golang/go/issues/26622
(Not sure if that's the best link, but it is easily Googleable.)
Generally, I’d say that dumping VM state (if not in the host executable format) is a perfectly ordinary thing for an isolated VM to do. It’s the default way Smalltalk systems operate, for example, being very close ideologically to Lisp machines in that they try to be an OS centered around a programming language more than a programming language implemented inside an OS.
(I should really look into that Oberon thing one of those days...)
It gets a bit worse if your initialization code is doing a lot of dynamic allocation, as you're shipping
Unless maybe you're managing cache yourself with a service worker.
Recently Emacs has moved to a "portable dump" approach which saves the heap snapshot separately.
https://web.archive.org/web/20191030205003/https://dancol.or...
> Nick Fitzgerald — Hit the Ground Running: Wasm Snapshots for Fast Startup > > Don't make your users wait while your Wasm module initializes itself! Wizer instantiates your WebAssembly module, executes its initialization functions, and then snapshots the initialized state out into a new, pre-initialized WebAssembly module. Now you can use this new module to hit the ground running, without waiting for any of that first-time initialization code to complete. This talk will cover the design and implementation of Wizer; discuss its performance characteristics and the scenarios in which it excels and when it isn't the right tool; and finally, in the process of doing all that, we'll take a closer look at what makes up the guts of a WebAssembly module: memories, globals, tables, etc.
Lots of interesting talks at the recent WebAssembly Summit! https://2021.webassembly-summit.org/
Besides that, I think Wizer [1] is both an elegant and a simple solution to speed up startup times with Wasm. So good work on that!
The original is at least grammatically correct.
I highly doubt that it's possible to beat the performance of JavaScriptCode (in JIT mode) with any WASM-based implementation.
More generally, WASM is promoted as universal bytecode virtual machine, so they want to make it "run everything everywhere".
But i'm not so sure about personal devices. It would be very hard to beat Javascript code on the Web in general. So i dont know about the general purpose target future in that case, i think it will flop, except as a fancy accelerator or to virtualize things that was once outside the web.
WASM does not provide a garbage collector, as another example; this probably makes non-garbage-collected languages behave more predictably.
I don’t think it is anywhere close to the truth. They are very well suited, as the JIT compiler can specialize dynamic types (and optionally deoptimize them when the type changes). There are also clojure, jruby, a python implementation, and java can also be written with significant use of reflection.
And then there is GraalVM built on top of the JVM that has truffleruby, the fastest Ruby implementation, graaljs which has very comparable performance to v8 with comparatively much less man hours , etc, all very dynamic languages.
All of the examples you‘ve mentioned don‘t seem like trivial ports at least from an outsider‘s point of view.
The JVM itself has definitely adapted to these use cases, but it wasn‘t designed with them in mind.
If you think the jvm is bad for dynamic languages, wait'll you hear about wasm!
In fact, I would expect the jvm to work much better for dynamic languages; not only does it already have a[1] gc, it has inline caching built in, which is frequently crucial for getting good performance in dynamic languages. (Though granted, as the niblings hint at, this requires type inference without invokedynamic.)
1. Arguably 'the'. I don't know of a platform with a better one.
JVM wasn't the first nor the only bytecode format around.
The early ones go back to 1961, and there are plenty of multiple language ones.
There are multiple runtimes that allow running Wasm without requiring a whole JS engine (Wasmer, WAVM, wasm3, ...)
Webassembly is getting a lot of adoption in non-browser environments. Be it extensions for larger applications (like Envoy) or "serverless" style microservice or server deployments.
There already are plenty of stand-alone WASM implementations . (wasmtime, wasmer, wasm3, SSVM, ...)
https://github.com/justjake/quickjs-emscripten#quickjs-emscr...
I’m aware of server side rendering but I suspect the performance in the client may be better responsiveness, with a failover to the server if it’s faster to provide a query response, but is there anything I should _really_ consider for client JS performance at that level?
You can access indexedDB from the worker, though. So you load the DB from the main thread, then you can do the filtering from the worker. You could return indexes then fetch those from indexDB on the main thread.
None of this will help with parsing large strings though, you are probably stuck with doing that on the main thread. And it is also not going to reduce the total time to perform the work. But hopefully it will avoid pinning the main thread, because that is bad.
I really enjoy this stuff, so feel free to contact me if you wanna talk it through. My email address is in my profile.
[0] SharedArrayBuffers were recently re-enabled according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I don't think this is correct. You can pass lots of types in via postMessage(), including Objects, ArrayBuffers, even a Canvas context. Some objects are transferrable as well, that is, zero copy.
As far as your point about still being stuck on the main thread, that may indeed be the case depending on the work.
More info:
https://stackoverflow.com/questions/19152772/how-to-pass-lar...
> Apps that browse the web must use the appropriate WebKit framework and WebKit Javascript.
Also Section 4.7:
> only uses capabilities available in a standard WebKit view (e.g. it must open and run natively in Safari without modifications or additional software)
[0] https://developer.apple.com/app-store/review/guidelines/
How legislators can easily spot problems in bad company practices that forces monopoly in material objects but are so obnoxious to everything digital?
I see even tech veterans here struggling in seeing that there's a big problem in all this..
Its like Microsoft not only shipping IE with windows, but making web sites slow in other browsers on purpose so at least in Windows nobody would use the IE competitors.
Why Apple is getting away for more than 10 years to such shady practices is beyond comprehension.
And this is just one of them.
This looks like it will fix a major point: I could compile everything that is typed and left the rest for runtime.
I'm wondering how much easier could be the life if I compile to WASM to be run on my host...
https://github.com/wasmerio/wasmer
(Haven't fully grasped how they're different from wasmtime, but I think they've spend more effort on embedding the runtime into other languages)
I think there's a world where true time-travel debugging is possible because you execute your Python program inside of a WASM VM & with WASM, you can save the memory/local registers/etc. and do true backwards/forwards execution.
But Java is one of the few major languages that is standard-specified, not specified by the reference implementation, the reference impl. is completely open source, and the ecosystem is blooming. So, no? It is a huge platform running a very very significant chunk of all the backend servers.
And safety has nothing to do with backwards compatibility, and the JVM is not unsafe at all against the attack vectors it is planned for.
"Everything Old is New Again: Binary Security of WebAssembly"
https://www.usenix.org/conference/usenixsecurity20/presentat...
The new applets was Flash with their new format that supported C++ as well, Silverlight and PNaCL.
Three little notes jump out at me, first:
> WebAssembly doesn’t allow you to dynamically generate new machine code and run it from within pure Wasm code.
This is a surprise to me. For some reason I thought wasm code could be a SharedArrayBuffer. And I thought that would be mutable. You might maybe have to round trip back to JS to modify this? But I thought it was read-write. I'm probably wrong but this was quite a surprise to me & a bit of a shocker to hear! Although there's good security things you get from this, I didn't expect it to be hard set.
Second thing that jumps out at me is a little fear. I look at the various projects out there intent on replacing the common DevTools debuggable/extension-able web page with a bunch of animated pixels, via use of Canvas, like Google's CanvasKit renderer for Flutter. To me, this is scary territory, because it de-empowers the user. This project here has uses far beyond the web, that's it's real purpose I think (who wants to load the spidermonkey engine compiled to wasm to start running their js on their site), but it still just makes me a little scared of the common DevTools experience fracturing & shattering, into many pieces. This project isn't uniquely scary, versus Go on WASM, or Rust on WASM, but it's still something I'm nervous about, and this article made me think of how easy it would be to start making the JS we run considerably harder to wrangle by people writing extensions or enhancing their user agent.
Third,
> Because the JS engines used to create isolates are large codebases, containing lots of low-level code doing ultra-complicated optimizations, it’s easy for bugs to be introduced that allow attackers to escape the VM and get access to the system the VM is running on.
Color me a little skeptical about the virtue of adding another security layer into the virtual machine. I really hope we can get good at isolates, to build blisteringly fast systems, and that we don't need to have multiple (or sometimes 1) processes on a computer, each running multiple isolates, each running multiple wasm containers. It's a cool capability, but I feel like ideally we'd be better off with less levels of nesting to get down to the real "process" (process->isolate->wasm-container), & better able to trust & leverage & use isolates really well to sandbox work. It seems to be working very very well for those with neat edge tech like CloudFlare Workers.
That said, I just did a quick search & a year ago someone was saying V8 isolates take ~3MB of memory each, which is far from insignificant. Developing AOT js->wasm tech could potentially have a huge memory-saving benefit. Ah, ok, good, the lightweight nature of wizer'ed containers is emphasized fairly well in this post! It didn't leap out at me the first time but it's definitely there! This is the core possible advantage, besides simply enabling better exploration of the problem space, which is also key.
Fourth,
> We started with making initialization fast with a tool called Wizer. I’m going to explain how, but for those who are impatient, here’s the speed up we see when running a very simple JS app.
This is a huge focus of this post, and it's a great technical marvel. On the other hand, the post talks about the advantages of how Wizer can use snapshots to skip initialization & start with a pre-booted application... the thing is: v8 Isolates can do that too. Deno has been doing quite a lot of work figuring out how to wrangle & manage & make effective use of V8 Isolates snapshots, for example, and that's one of the core reasons I think it is an impressively promising advancement & upcoming core technology. Deno is really trying to wrap & better expose the V8 runtime in a better managed fashion, and I think we'll see many of the advantages claimed by Wizer start to get reported out the world via Deno as well, over time.
No, this was an explicit design decision for security reasons. Data is mutable, but you cannot exec it.
> You might maybe have to round trip back to JS to modify this?
Yes, this is technically possible but you're unlikely to see savings. I'd suspect a "JIT" that instantiates a WASM via the JS API using generated bytes from another WASM call won't help much (the "exports" would have to be JS calls too or you'd have to re-instantiate all the WASMs together to link exports).
I think this focus is about RUNNING the wasm. If you have are a host, I think you can generate wasm inside it and pre-cache the "std library" or something, this is what I infer from this idea (from other users that wanna implement it).
Because certainly you can generate code with control of your host:
Hmm, would be great to have this for clojuresscript as well!