It's not a secret that you can write JS which will be JIT-ed into an extremely efficient machine code. But it's a "secret" - how to write that JS. You would need seriously advanced hackers who can study V8 assembly output and correlate it with used JS features. And do it all the time, when someone changes that code. Or may be even unrelated code changes will change the way V8 compiles that particular code snippet. JS compilation is black magic.
On the other hand, writing C is boring and solved problem. Compiling C to wasm works. It's predictably fast. You can use it for performance-critical code and it'll probably work without any adventures to V8 internals.
1. Create an object with a fixed number of keys and NEVER add keys, remove keys, or change the data type of a key's value.
2. Make arrays of a set length and only put ONE type data type inside. If that data type is an object or array, all the objects/arrays must have the same type
3. Functions must be monomorphic (always called with the same parameters in the same order of the same type)
Do this and your code will be very fast. Do something else and it will get progressively slower.
Running the profiler in Chrome or Firefox is very easy and it will show you which functions are using up most of your processing time. Focusing on applying these rules to just those functions will usually get you most of the way there.
There's nothing you can do to guarantee your JS isn't passed inputs that trigger pathological cases. There's no linter that can guarantee that you're writing code in a way that is the fastest it can be (even with type checking!). Asking developers to be a human linter for the sake of consistent performance is a bad developer experience no matter your skill level.
WASM simply doesn't have this problem.
why doesn't this problem also apply to WASM?
I wish TypeScript helped more here. I'd prefer if it had a performance option that disallowed or at least warned about these kinds of things.
Which is what wasm will, hopefully, give us in the long run. And ensure that said PL will have to remain competitive against the new contenders, since they can always replace it.
Not once in my entire life have I heard anyone say this
Almost always its either about it being faster or so they can use a language that isn't JS. And both are those have dubious value because I seen wasm be slower and people complain a lot about lack of tool support. Which is why the other day I claimed very few people use it. I seen many try it once or twice and not want to go through it again
Afaict it's much easier to write a high performance JIT for WASM because those cases aren't possible. And consequently, it's easier for something compiling to WASM to get high performance out.
I imagine Python performance isn’t too great either.
Whereas if you compiled (or translated — not sure how comparable the WASM instruction set is) x86 bytecode to WASM, it’d be a walk in the park.
Another use case I’ve toyed with is date time. Specifically trying to figure out if something like the rust Chronos crate is a better fit for crunching and calculating dates than something like date-fns or Luxon. Not sure about this one yet.
1. Browser support - its not there yet. you'd have to polyfill. A production level polyfill is 16 KB, and is still very nasacent, and, on top of that, requires support also for BigInt[0]. The polyfill that tc39 put out is decidedly marked as non-production ready[1].
2. Polyfilling - as mentioned above, we have to deal with polyfilling the API, and that isn't a clear and easy story yet. WASM support goes back farther than this.
3. Size - its entirely possible to get WASM builds under 16 KB, and the support is better, espcially for operations on strings and numbers (dates fit this category well). The only complication I haven't quite solved yet is:
A) Can I validate that a WASM build will be under 16 KB. This is crucial. I'd even accept it at 20 KB because of wider browser support[2]
B) Can I fall back to asm.js if needed (there is a slim range of browsers that support ASM.js but not WASM, mostly pre-chromium Edge[3]
C) Is it performant compared to something like Luxon or date-fns? WASM excels at string / numerical operations so my sneaking suspicion is yes, at least in terms of the WASM operations. The complexity will be serializing the operations to a JS Date instance, Luxon & the Intl API might be most useful here
[0]: https://github.com/fullcalendar/temporal/blob/main/packages/...
Don't forget WASM doesn't provide direct access to any OS time APIs (timezone info, current time, regional time change modifications) so the solution will still basically boil down to "call Date() and polyfill a better library" except now you have extra code to ferry the data back and forth to do a few string and math ops. Unless the use case is processing very large datetime datasets in one call the JS<->WASM function call overhead for all of this will probably take the majority of the execution time.
Not to mention after you get all of this solved, tested, and deployed you know as soon as Chrome starts shipping Temporal the cool custom solution becomes 50% slower for the average user despite all the effort because you didn't just use something like a Luxon which automatically updated to use Temporal on release. This may just be me being lazy though :p.
strings & numbers are WASMs strong point, so if you can pack the locale information tightly in a binary format, you might actually win out in the medium term. This shouldn't be a years long project by any means. And frankly, with the way enterprises move, you'll always have some client (at least in my business) where I need to support some modernish browser that may not have Temporal, so if this is more performant (we do alot of date time datasets, so yes, thats part why I'm looking at this) why not?
It could also be the wrong solution. I'll found out one way or another.
WASMs strong point isn't necessarily "strings and numbers" it's running large amounts of compiled code on large amounts of data. Video processing, PDF readers, video games. As an example even computing a large image the Mandelbrot fractal (pure math workload) then passing back an arraybuffer of the pixels was faster in JavaScript until WASM SIMD+Threads finally landed and JavaScripts poor parallelism finally factored in. Doing it with a functional call per pixel JavaScript is still ahead of even WASM with SIMD due to the functional call overhead.
But all that said I think it's a really cool project to try and I hope you're able to build what you're seeking. If you do be sure to post it to HN so I can check out how you managed to pull it off :).
That is comparing WASM+JavaScript vs pure JavaScript. Unsurprisingly there’s some interop overhead. Those benchmarks are not relevant if you’re not using JavaScript (e.g. WASI stuff) or you’re doing the bulk of your calculations in WASM and not rapidly jumping back and forth between WASM and JavaScript.
The second benchmark isn't measuring the performance of Javascript, but the performance of the Javascript sort() call (which most likely is implemented as native code).
In general, WASM should be both in the same ballpark as portable natively compiled code (e.g. not using SIMD), as well as Javascript which has been written for performance (which also means that well written - but non-idiomatic - Javascript can be in the same ballpark as portable native code).
The main advantage of WASM versus JS isn't mainly performance, but predictable performance (because the GC is taken out of the picture, and the linear memory model), and that WASM is a better compilation target than JS.
Java introduced a language and library… but the real innovation was that it introduced a cross platform VM that was supported by some organization
WebAssembly is now doing the same thing… but just the cross-platform VM part
Now you can run Python and JS on the JVM these days but these are not de-facto implementations and so their adoption is pretty low. I wonder if the same issue will apply to these alternate WASM implementations of existing languages.
WASM was an evolution to say rather than do all that why not just have a way to tell the browser's VM what we want to do directly. Now instead of having to parse JS syntax to find type hints and so on the browser can just parse pre-encoded bytecode. Instead of having to understand certain logic is trying to emulate functionality like 64 bit integer multiplication and optimize it out the browser can be told to do a 64 bit integer multiplication directly. Since this is a separate interface from JavaScript it allows work on things like threads, SIMD, and garbage collection to not worry about how JavaScript has a hard time with these concept since JavaScript is not the base anymore.
JIT-compilation with optimisation (and de-optimisation!) is costly, so browsers tend to only interpret Javascript the slow way at first, enabling each (higher) tier of compilation only after run-time profiling. With higher complexity comes higher risk of errors, and there have been a number of serious vulnerabilities in browsers' Javascript JIT-compilers in the last decade.
Not many companies have the resources to develop a high-performance Javascript engine that can compete with the best.
Also, writing optimised Javascript code so that it gets made into fast JIT-compiled code is a black art.
WASM on the other hand, has been designed so that it could be assembled into machine code straightforwardly in a single pass using little CPU time. You'd get native performance straight away. (Not that optimising WASM runtimes don't exist)
WASM has continued to evolve past what's possible in JS/asmjs since that article as well, with things like SIMD support
E.g. if you had only done rendering on a CPU and someone came by and said "we can do all sorts of stuff we couldn't do before with this GPU check it out!" it'd be easy to say "I could do all that on a CPU" and you could even show the exact same benchmarks presented here and then say "see, the CPU even runs the single threaded factorial function many more times per second than this new GPU". Everything you said would be absolutely correct in the most literal form yet it'd still be completely missing the point of why the GPU was made and how to assess if it fits that purpose better.
Then someone shows you the GPU doing rendering it was designed to do well better than the CPU and the reply is "So it is about the GPU being faster than the CPU?". Yes. No. It depends what context you're asking from. Traditional use cases no, what it was designed to do well yes.
The original article which talked about WASM being slower itself specifically notes this relation of purpose, functionality, and performance it's just tucked away in the conclusion:
> definitely don’t go converting all your websites’ JavaScript to WebAssembly! However, that’s not really the aim of WebAssembly. Its aim is to enable richer experiences on the web that require higher performance, for example machine learning, virtual reality, or gaming.
Whereas Asm.js had (has?) perfect backwards-compatibility with unsupported browsers and JS interpreters, WASM requires users to remain on the bleeding edge of new browser features as it continues to evolve, and introduces a whole host of fantastic new bottlenecks as the designers puzzle over how to interface WASM modules with the rest of the facilities JS can already access.
The whole thing is a hilarious boondoggle- an insane amount of effort and complexity for mild bandwidth and page-load time savings- made all the more hilarious for the fact that a remarkable number of people seem unaware that Asm.js ever existed in the first place.
> definitely don’t go converting all your websites’ JavaScript to WebAssembly! However, that’s not really the aim of WebAssembly. Its aim is to enable richer experiences on the web that require higher performance, for example machine learning, virtual reality, or gaming.
WASM functions aren't meant to replace small JS functions on your standard website. It's meant to be a general purpose VM you can target large amounts of non-webpage code to.