Maybe you don't need Rust and WASM to speed up your JS
mrale.ph
mrale.ph
All optimizations in the post can mostly be divided into three large groups:
1) algorithmic improvements; 2) workarounds for implementation independent, but potentially language dependent issues; 3) workarounds for V8 specific issues;
You need to think about algorithms no matter which language you write in, so we don't need to talk much about the first group. In the post it is represented by sorting improvements (sorting subsequences rather than the whole array) and by discussions of caching benefits (or lack of them there-off).
The second group is represented by a monomorphisation trick: the fact that the performance suffers due to polymorphism is not really a V8 specific issue and it is not even JS specific issue. You can apply this approach across implementations and even languages. Some languages apply it in some form for you under the hood.
The last group is represented by argument adaptation stuff.
Finally an optimization I did to mappings representation (using typed array instead of an object) is an optimization that spans all three groups. It's about understanding limitations and costs of a GCed system as whole.
Now... Why did I choose the title? That's because I think group #3 represents the issue that should and would be mostly fixed over time. While groups 1 and 2 represent universal knowledge that spans across implementations and languages.
Obviously it is up to each developer and each team to choose between spending N rigorous hours profiling and reading and thinking about their JavaScript code, or to spend N hours rewriting their stuff in a language X. What I want is:
a) that everybody was fully aware that the choice even exists; b) language designers and implementors worked together on making this choice less and less obvious - which means working on language features and tools and reducing the need in group #3 optimizations.
Here's a crazy thing I recently learned: apparently monomorphism isn't just "object with identical keys", apparently (at least in Chrome), the order in which you declare those keys matters. According to this presentation from 2015[0], adjusting the following lines in the Octane/Splaytree benchmark so that node.left and node.right are always assigned in the same order resulted in 15% better performance:
var node = new SplayTree.Node(key, value);
if (key > this.root_.key) {
node.left = this.root_;
node.right = this.root_.right;
...
} else {
node.right = this.root_;
node.left = this.root_.left;
...
}
Now, I assume that this out-of-order thing was actually done on purpose, to benchmark how the JIT handles code like this. Further evidence for that is that the SplayTree constructor[1] does not feature a left and right key either: SplayTree.Node = function(key, value) {
this.key = key;
this.value = value;
};
Still, I wouldn't be surprised if it was common for real-life code to accidentally have objects that should have the same hidden class end up with different ones because of this.[0] http://mp.binaervarianz.de/fse2015_slides.pdf
[1] https://github.com/chromium/octane/blob/master/splay.js#L390
Although I suppose you're already required to have two separate hidden classes to distinguish these two kinds of objects anyway.
> with a bizarre exception for arrays
Wow, you weren't joking with how bizarre this gets:
var a = {};
var b = {};
a.a = 0;
a.b = 1;
a[0] = 0;
a[1] = 1;
b.b = 1;
b.a = 0;
b[1] = 1;
b[0] = 0;
Object.keys(a); // Array [ "0", "1", "a", "b" ]
Object.keys(b); // Array [ "0", "1", "b", "a" ]
PS: Thanks for making a great shell :)Chrome/V8's team found they could get substantial performance improvements by diverging from the de facto standard, without too much of a cost to web compatibility.
See: https://stackoverflow.com/questions/5525795/does-javascript-...
The order is only sometimes guaranteed, of course, because JavaScript. (But critically, for this discussion, it is important that it is sometimes guaranteed, because it forces that information to be stored by the VM.)
// the keys of an object
export function keysOf(obj) {
let keys = Object.keys(obj);
keys.sort();
return oneOf(keys);
}
Context: the rest of the code takes an object representing a schema, and creates two functions. One that can turn any object fitting that schema into an array, with positions indicating which key they originally belonged to, and another one that can reverse the process.Could this also explain why I have no consistent order of properties when viewing state with the Redux dev-tools? Instead of Object.assign I use my own simplified merge code[1]. Maybe if I also make that use a sorted set of keys, the devtools will become more consistent in their presentation (and it might result in more consistent hidden classes too).
[0] https://github.com/linnarsson-lab/loom-viewer/blob/master/cl...
[1] https://github.com/linnarsson-lab/loom-viewer/blob/master/cl...
I suspect some respondents will say, or believe, that this falls under the category of "you have to know the VM/runtime intimately to get good performance".
I don't think that's true. If you know the general problems with polymorphic call sites, you can (in JS and many other languages) check to see if the runtime is applying optimizations of this kind by being explicit. If it helps, you can get a free optimization by setting explicit arity/dispatch just because you happened to know about the issues surrounding polymorphic dispatch. That's a case of fundamental knowledge speeding up/improving your progress; not a case of "you need to know the VM guts like the back of your hand to make code fast".
The key takeaway of your commentary on asm.js five years ago (http://mrale.ph/blog/2013/03/28/why-asmjs-bothers-me.html) is the same as the key takeaway from this post: we haven't reached the end of JS engine performance improvements, and if we apply the same rigor to JS development that we apply to C++ or C or Rust or some other language the results are definitely surprising!
https://github.com/WebAssembly/host-bindings/issues/11#issue...
If you think the assembly in this article is bad, WASM/host-bindings appear to be even worse.
The point of rust+wasm benchmarks is that one can write reasonable, maintainable, functionality-focused code (not to mention all the rust-specific benefits) and get good performance out of the box.
Radix sort for instance comes to mind.
This isn't unique to this situation either. If you're writing Python and your choice is to either deep dive into various hacks to maximize performance of your pure python or learn Rust/C/C++ and call out using an FFI mechanism, if you have the time to space, I think it's almost always better to learn the new language. There are many benefits to learning new languages beyond just the different performance characteristics, so if you can afford to take that path, I think it's usually a good choice to do so.
As far as I know Rust has just two major success stories right now: Firefox 57 (Servo) ripgrep (even included in Microsoft Visual Studio)
Here's a bunch more. Dunno what your definition of "major" is though: https://www.rust-lang.org/en-US/friends.html
C++ had the advantage of being immediately adopted by the OS vendors for GUI development, although nowadays, with exception of Windows, its role has changed into just addressing the GPU.
So maybe one day we will get something like shaders, CoreGraphics, DirectX, SurfaceFlinger in Rust, but it will still need a couple of years.
Webrender is already something into this direction.
I wouldn't say this is entirely wrong, but proper cross-platform package and test management with Cargo is a reason alone that utilizing existing code is waaay easier in Rust. C++ has more existing code though, so they might hit your niche needs better.
In what concerns Windows development, NuGET and vcpkg are already a big improvement.
I think people think too lightly of rewrites. Actually, I'd even argue that in some cases, rewrites are done because they're easier than actually fixing the problem. Yes, Rust and other languages will give you better performance out of the box, but at what cost?
I agree that rewrites are often taken too lightly, but if they address the original problems I think it would be more accurate to say that they are often needlessly expensive ways to solve problems that can also be solved in other, cheaper ways.
I'd also point out that in the case of many open source projects, finding an optimization consultant is not even remotely an option. For many of those projects, if performance is suffering, someone needs to step up and figure something out. Then the question becomes which approach can be applied by some contributor who's actually willing to do it. If you don't have someone who understands polymorphism in VM runtimes, I think in many cases you'd be well served by sprinkling some wasm on the problem. Of course this doesn't apply in all cases.
Yes. I wrote about this in some detail 2 years ago, Jitterdämmerung:
So now if google changes the back end, websites will slow down, which means that Google has to ossify implementation details, furthering this sort of black magic into CS lore forever.
This, honestly, is what I hate with modern dev. A few days back there was a discussion on how programming is hard nowadays.
Really, programming is more accessible than ever. What is hard is the "black magic" that's becoming more and more prevalent, most of which is implementation defined, that everyone is expected to know (and really, most don't really know anything. They just repeat rumors they read about online, often years out of date).
Writing code based on gut feeling and urban myths was never a good idea.
You might have some high performance C code only tested in gcc on GNU/Linux, and then get some nasty surprises when testing on HP-UX with aC, for example.
Back in the day, DDJ and The C/C++ User's Journal used to run articles comparing the quality of all most well known compilers.
That does leave out Safari, Edge, Opera, and probably some other obscure browsers, and doesn't help with your point about future engine changes potentially breaking the optimizations.
> A bit of mathematics can guide us (100000log100000 is 3 times larger than 3333×30log30)
You have to be careful doing this kind of analysis. Big-O is explicitly about what happens for large values of N. It is traditional to leave off smaller factors, because they don't matter at the limit of N going to infinity. There is implicitly a constant coefficient on each component, and that might matter more at small N.
So e.g. I've seen cases that were O(N^2 + N), and which of course you'd traditionally write as O(N^2), but where the O(N) factor mattered more at small values of N because of constant factors. Depending on whether you cared more about small or large values of N, would guide whether you'd actually want to go after the O(N^2) factor or not. If you just blindly went for the larger factor, you could waste a lot of time and not actually accomplish anything.
It's precisely the unpredictability of JIT-driven optimizations that makes WASM so appealing. You can do all your optimizing once at build time and get consistent performance every time it runs.
It's not that plain Javascript can't be as fast -- it's that plain Javascript has high variance, and maintaining engine-internals-aware optimization in a big team with a long-lived app is impractical.
Such a tool would give you the benefits you're praising about the WASM compilation workflow: Separately maintained, engine-specific optimizations that can be applied at build-time and don't mess up the maintainability of your source code.
The advantage of WebAssembly is supposed to be (I think) that it's simpler and will give more consistency between browsers, so browser behaviour will be less surprising, and you can thus get away with compiling a single version.
And if you take this approach of compiling JavaScript to a simple and consistent subset of JavaScript that can be optimized similarly in all engines, you'd end up more or less targeting asm.js, the predecessor to WebAssembly. :)
What could a tool like you're describing do that the engines don't do themselves?
(Note: I'm glossing over the case where one is using bleeding-edge syntax that a JS engine doesn't yet know how to optimize. In that case preprocessing out the new syntax is of course very useful, but I don't think this is the kind of optimization the GP comment was talking about.)
As an example for something that static analysis could have caught, take the example from the article about the "Argument Adaptation"[0]. Here the author uses profiling to learn that by matching the exact argument count for calling a function, instead of relying on the JS engine to "fix that", the performance can be improved by 14% for this particular piece of code. Static analysis could have easily caught and fixed that, essentially performing a small code refactoring automatically just like the author here did manually.
[0] http://mrale.ph/blog/2018/02/03/maybe-you-dont-need-rust-to-...
Also, remember that JS engines are not all-powerful and all-knowing in their optimization techniques, it's still just a limited number of individually imperfect humans working on them, just like the rest of us. So naturally there are going to be opportunities for other humans to utilize and help complete the picture and increase the overall value and effectiveness.
Maybe in this case there is room specifically for a JS-syntax-level tool that also has more freedom in terms of execution time and related concerns, because it can execute at build-time, potentially pull in or bundle much more information about optimizations with it (imagine using (potentially expensive) ML model to search for probable performance wins, actually do micro- or macro-benchmarks during the build step), be maintained outside of the implementation of a JS engine and thus have the additional benefits of a potential broader contributor base and a faster and more focused release cycle, etc. Or this may not be a good idea after all. I cannot tell. All I know is that if we actually do see that there are and will continue to be optimization gaps in the JS engines themselves, then there is a way to fill them, and likely without having to switch to basically entirely different (frontend) technology stacks.
I think you're mischaracterizing what happened a little. Most of the author's improvements weren't engine- (or even JS-) specific, they were algorithmic improvements. But for the first two that were engine-specific, it's not like he applied a rote transformation that always speeds up scripts when you apply it. Rather, the author (himself a former V8 engineer) saw from the profile results that certain kinds of engine optimizations weren't being done, and rewrote the code in such a way that he knew those specific optimizations would take place as intended. Sure, a deep ML preprocessor might do the same - but only after trying 80K other things that had no effect, and on code that wasn't even hot, no?
More to the point though, it strikes me that you say JS engines aren't all-powerful, but in the same breath you seem to assume that just because V8 didn't optimize the code in question that it can't. It seems very likely to me that any case you can find where a preprocessor improves performance is a case where there's a fixable engine optimization bug. Sure, in principle one could build a preprocessor for such cases, but it seems more useful to just report the engine bugs.
The difference between asm.js and WASM is mostly just that WASM is more compact and easier to parse, while asm.js is a more gradually compatible upgrade story.
Once it tries to start inlining on the client side, that will open the floodgates to other optimizations.
The compiler would then spit out eighteen different versions and the right one would be downloaded by the user.
At the end of the article, the author wisely chooses to move some objects out of the control of the GC:
We are allocating hundreds of thousands Mapping objects, which puts considerable pressure on GC - in reality we don’t really need those objects to be objects... First of all, I changed Mapping from a normal object into a wrapper that points into a gigantic typed array...
This suggests to me that we are no longer programming the way one usually does in a dynamically typed, garbage collected language -- and thus it might still be the right decision to move to something like Rust (or Swift or Go) where there is considerably more control over allocation.
The author is able to achieve a speed-up of ~4x which is close to the 5.89x achieved by the Rust implementation. There are benefits to having all ones code in the same language; but there are also benefits to switching languages to obtain better ergonomics and safety properties.
This is not a fair comparison because the author made algorithmic improvements that would also improve the wasm version if applied to the Rust code. Source: https://twitter.com/mraleph/status/965616993310265344
In reality it means that performance of my code should not be that far from what WASM is showing because sorting of originalMappings (which I do eagerly and WASM version does lazily) is one third to one half of the overall runtime.
I will try to measure and update the post tomorrow or Wednesday.
I do agree that the way I manage mappings is hardly ergonomic. As mentioned in the post I would prefer to use Typed Objects to access the packed array of mappings, but alas that proposal is stalled.
[1] https://github.com/mozilla/source-map#sourcemapconsumerproto...
EDIT: I noticed it's actually mentioned in a footnote in the article - the Typed Objects API was really nice, I had a chance to use it for a prototype.
As far as WASM is concerned I'm more excited about the possibility to run any programming language on the web than the raw performance gains. So far it is still year(s) away from this goal(i.e. lack of web APIs/DOM access makes it useless for web dev).
In a sense, you pay those hours that you saved up by using an easier, more intuitive language like JS. Or you can choose not to and still have pretty good performance and blazing fast dev cycles.
As far as the performance is concerned I believe profilling the internals of the VM(as the author does) is not a good fix on the long term.
It's the same argument any junior engineer makes when they start at a company with a legacy codebase. They always want to start from scratch but don't realise the cost or the fact that while they'll avoid 100 mistakes of the past, they'll also make 100 new ones. Why not just make JS better? It's already happening year by year.
If we could have been able to challenge it by allowing another language in there I would like to think we wouldn't be facing a lot of the issues we do now.
The extent to which WASM could be used in the Node ecosystem would essentially be an indictment of how badly the Node community has bungled FFI. (node-ffi[0] should absolutely be added to Node core, not be independently maintained and always trailing compatibility with newer node releases, right now you have to choose between security or compatibility)
"WASM will eventually allow the same choice in the browser."
When will that actually be? That's the big question.
I wish that was the case. Yes, I know it is only receiving security updates -- but it will supposedly get those until Windows 10 is no longer supported (if I'm reading things right).
Unfortunately, that doesn't stop a bunch of people from using it. It still has a considerable amount more marketshare than Edge does (if netmarketshare.com is to be believed).
It's compile-time safety. Virtually none of the type knowledge exists in the runtime. Still, it's far better than nothing, with the self-documenting nature of typed code being one of the biggest wins IMO.
The same way it transfers when the language is compiled to x86 instructions! By adding a pre-written and pre-compiled runtime.
For example, the way you allocate memory in WASM is to link in a malloc() implementation. This malloc lives in the same linear memory block, and so its internal data structures are vulnerable to being smashed, just like classical heap smashing.
One improvement is that the call stack is maintained externally, so it is not possible to stack-smash a return address. Still heap function pointers are vulnerable to a return-to-libc style attack.
Round and round we go.
So the correctness transfers in the sense that the code the compiler output should always be correct.
[1] https://www.typescriptlang.org/docs/handbook/advanced-types....
Well, your understanding is wrong. Sum types satisfy a specific universal property, which union types don't.
(0) There exists a procedure “foo” that recovers the functions from the summands, given a function from the sum.
(1) There exists a procedure “bar” that recovers the function on the sum, given the functions from the summands.
(2) “bar . foo” is the identity on the space of functions from the sum.
(3) “foo . bar“ is the identity on the space of tuples of functions from the summands.
It is easy to see that, if your definition of “sum” is “a JavaScript tagged union”, and your definition of “function” is “any JavaScript function”, such functions “foo” and “bar” are not constructible.
Tagged unions only work under very stringent conditions:
(0) Tags never overlap. Have fun enforcing this.
(1) Tags are only used to discriminate between cases. Again, have fun enforcing this.
Moreoever, any talk about universal properties being satisfied is utterly pointless when object identities permeate the semantics of the language.
> Tags never overlap.
Agreed that TypeScript doesn't enforce this property of the type definition, and it would be nice that it did, although my interpretation of the claim was "any sum type is expressible as a tagged union". I certainly don't claim that TypeScript has a syntax for describing tagged unions and ensuring that they're well-formed; it's just that they're expressible via other features.
> Tags are only used to discriminate between cases.
It's true that tags are string values in my case, although possibly a fancier version could use opaque values. But given that TypeScript enforces that the type is either 'A' or 'B', couldn't any other language derive that string from the case and use it?
Do you view (say) SML datatypes as being valid sum types? I'm having a hard time understanding when SML types would be strictly more powerful/expressive than TypeScript here.
https://www.typescriptlang.org/play/index.html#src=type%20A%...
I think you missed the word “broken”, before “languages”. Sums in Standard ML are really sums.
> similar to how it's nice to just pretend that computers work on real numbers instead of floats.
Computers can totally work on real numbers. (Okay, not all of them. But way many more than floats allow.) For example, you can implement an abstract type whose internal representation is Cauchy streams of rationals, and only provide operations that send equivalent streams to equivalent ones. Of course, this would be horribly slow, and few people actually need exact real arithmetic anyway.
> In this case, maybe you'd need to pretend that JS works on values rather than having object identity.
JavaScript does work on values. The values are the object identities.
> See the link at the bottom for what I think foo and bar would be in TypeScript. I'm curious if you see specific weaknesses in it.
You anticipated it yourself:
https://www.typescriptlang.org/play/index.html#src=type%20A%...
> Do you view (say) SML datatypes as being valid sum types?
Yes. On the other hand, Haskell's aren't, albeit for different reasons than the ones given in this thread.
> I'm having a hard time understanding when SML types would be strictly more powerful/expressive than TypeScript here.
In Standard ML, type abstraction actually works. You can hide the implementation of an abstract type, making it impossible for others to destroy the invariants that you worked so hard to establish. Or at least should have.
See here for why union and intersection types make type abstraction difficult: https://news.ycombinator.com/item?id=16399722
I'd say the best type systems are found in Rust (naturally models zero-cost abstractions but doesn't have dependent types) and Idris and Coq (have dependent types but don't naturally model zero-cost abstractions).
That's true in pretty much all languages. Even in Haskell you can use `unsafeCoerce`. In Rust you have 'unsafe' blocks. And any language which has a FFI you can implement the type-safety-violating functions in the foreign language (often C).
Instead TypeScript has a bunch of serious unfixed design flaws that make the problem pervasive, plus they refuse to fix them (see https://github.com/Microsoft/TypeScript/issues/9825 ).
MLs (OCaml, F#, Reason) are hardly "extreme functional spectrum".
That you have to return from statements is such a flow-breaking PITA, one that leads to ugly, verbose code. switch is particularly painful in this regard, would be wonderful to write `let foo = switch (x) { case ... }`, but no, it's a statement, not a value.
Also, structural typing has its strengths, but equality is not one of them. In order to generate a nominal type you have to hack in a private class member (what they call "branding"). This makes newtype/value class wrappers needlessly verbose.
TS feels somehow like a Java/Javascript hybrid with some more advanced type system features (e.g. union types) mixed in. Saying that, editor support (VS Code) and JS interop are pretty amazing.
(Really I'd like polykindedness as well. I've only needed it once in over a decade of programming but it was such an intensely frustrating experience to need it and not have it)
WASM isn't about performance. It is about writing applications in any language and importing those applications into an island in a web page.
No, wasm is almost always faster than JS, and that has been the case for a while.
Maybe you're thinking about some very specific aspect of performance or benchmark? (For example, wasm->JS calls might be slower than JS->JS calls in some cases.)
You don't really need wasm for that, do you? Anything compiles to JS nowadays and you'd actually get easier access to the DOM and GC and support for source maps (are those working for wasm yet?)
How did we end up in wasting so much time on trivialities?
Then MS gave us AJAX and 37 signals made it popular, until apps like gmail made it so mainstream it was impossible to go back to old static pages.
But it was too late. The shitty language we had was the only one available everywhere to do dynamic web pages now.
IE would not move, and Firefox and Opera were the underdog, spending their resources on more important things. So nobody tried to implement a better existing language.
When Google faced the challenge of creating chrome, they had to be compatible. So instead of implementing a better language, they also used JS, and injected millions of dollars into making the V8 so it has decent performances.
After that, JS was usable, and so we moved on.
Google maps. When Google maps first came out as a beta/preview and I first loaded it up, it was magic. Not magic as in the "this is wonderful" sense (it was) but in the "any sufficiently advanced technology is indistinguishable from magic" sense. I truly didn't understand how what they were doing was possible for the first few minutes. I'm pretty sure the feeling was the same with all the other programmers and sysadmins at the ISP I worked at.
Gmail might have clued a lot of people in as to how you could make webpages more dynamic, but Maps truly showed us how you could make a web application every bit as fluid and usable (if not more so) than a local one in the right circumstances.
They did implement a better language, Dart. But it was too late, like you said. Maybe we'll have a change of using it for mobile apps with Flutter.
They failed miserably.
And several other projects failed miserably at the same thing as well.
Either way, speed was not the main reason they created Dart.
Dart was designed for writing large web apps.
Dart's top design constraints was good interop with JavaScript meaning transpilation of Dart to decent JavaScript and consumption of existing JavaScript libraries from Dart.
I repeat: top design goal.
You can't take a language (be it Python, Ruby or Lua) that was not design with that constraint in mind and magically make it work.
There are transpilers from those languages to JavaScript but they are toys.
You just can't reconcile Python semantics with JavaScript semantics in a reasonable way.
So they did the next best thing: they designed a better Java/C# while keeping JavaScript interop as a priority.
Now Dart is morphing because the top design goal is to make it the best language for cross-platform mobile (i.e. iOS and Android) apps, which requires different trade-offs.
My guess is that they didn't invest anywhere near the resources on unladden shallow (which was probably a side project) than they invested on chrome vm (which was a core project).
Actually, I spend quite a lot of time on the Python mailling list, and here you can see they regularly find things to improve, perf wise. They just have terribly low resources.
I've rarely seen a project as popular as Python, used by so many rich huge corporation, which such little resources actually. It's heart breaking.
That's actually an interesting claim, and so I would really appreciate if you could provide a (possibly informal) proof that justifies it?
That being said, some of these optimization techniques completely took me by surprise. Defining the sort function as a cache lookup that converts the sorting template to a string and then builds an anonymous function out of that string which is finally used as the exported function seems, to me, like an extremely roundabout way to achieve inlining the comparator. And the argument-count adapter having such high overhead on V8 seems like something that should generate a warning for the developer.
The cache analysis and algorithmic improvements seemed fairly straight forward, but when you're at the point of having to manually implement memory buffers to alleviate GC pressure, you're diving below the level of abstraction that the language itself provides you. At that point, I think the argument to switch to a language designed to operate at that level of abstraction holds some sway.
However when I do optimize JavaScript code I often get 10-100x performance. Usually by writing better algorithms. Eg no "black magic". So the original code in the article is not that bad, considering he "only" got 4x performance.
Moving to another programming language / WASM for less then 2x performance is not worth it - unless you hate JavaScript.
Caching could have been originally faster, but became slower thanks to the continual improvement of the Javascript VMs.
I recently discovered that a JavaScript implementation of radix sort up to four times faster than the the built-in sorting algorithms for TypedArrays[0][1][2]. Imagine how much faster a good WASM implementation could be!
It also makes me wonder why browsers don't make use of the guaranteed memory layout of typed arrays to use faster native algorithms. Sure, Typed Arrays have to support the sorting API, which is comparison based and works with closures. But why not detect when someone calls sort() without any further arguments, and make that very common case use faster native code?
Because for me, this difference in performance made a difference: I am animating plots where I need to sort more than 100k elements each frame, and sort eating up 5ms or 20ms is the difference between choppy and smooth animations.
[0] https://run.perf.zone/view/radix-sort-uint32-1000-items-type...
[1] https://run.perf.zone/view/radix-sort-uint32-1000000-items-t...
[2] https://run.perf.zone/view/Radix-sort-Uint8Array-loop-vs-fil...
The faster sourcemaps can be parsed and manipulated, the faster the DevTools and build tools will execute.
For JS, I need consistency and stability, because I'm dynamically generating code. Usually I'm pretty satisfied, but sometimes after recompiling code I get a huge performance hit for no reason.
For WASM, I know I have stability, but I need faster load times. On my Chromebook, for example, it takes 10-20 seconds just to load the WebAssembly. If this problem is solved, I might move everything performance-sensitive over to WASM eventually.
To those saying "these are implementation defined optimizations" etc - you do the same exact thing in rust. I know some rust code is 'fast' and some is 'slow' and I have to understand rust and to some extent the state of llvm + rust. This is simply part of writing fast code, no matter the platform or language.
Nice writeup!
I don't want to write a webapp in Rust. It'll always be second class (though maybe that won't be a problem if the WASM apis get good enough...).
+1
It's also a v8 specific weakness, it's not hard to imagine a small fix getting rid of the performance penalty.
So in Rust you would be forced to write it the way you consider to be "bad idea".
Many people think that default function arguments are a bad idea and languages like Rust or C or Go or Java don't even have them.
https://twitter.com/mraleph/status/965686742614462466
I can update CSS if that helps.
UPD. Updated CSS to use non monospace fonts for the body.
Thanks for taking criticism in a healthy way.
Javascript is already very fast, but compiling to javascript feels awkward.
Since the beginning of computers, it should be trivial to distribute programs online efficiently.
The web is already platform dependent, but it also need to be fast and language independent.
> The author is able to achieve a speed-up of ~4x which is close to the 5.89x achieved by the Rust implementation.
I will do measurements later to compare and update the post.
I enjoyed the article :)
I'm a (junior/so-so) react dev. I like the language, I probably could get better at it but I've gotten to the point where I like the sound of my own music.
That said, my question is the following. There seems to be a lot of resources on the web of the type "Hey Rust and WASM is a thing! You can make webpages with it". Ok, fine. However, I don't see a lot of the things that are in libraries like Vue and React that offer me SPA, fast development time and component (OR!) functional pattern design (I won't mention the ability to add npm packages, because yeah, Rust/WASM doesn't have an ecosystem yet, so that might be punching a bit below the belt). Is there anyone out there making a React like or "eco-system builder"-esque platform that would give me a lot of the benefits I'm seeing with React but with increased performance?
Also, I've done some low level programming before and think that Rust is very cool for that (yay memory safety! yay error messaging (no seriously yay)!). However, and I may be betraying my ignorance here, if I want to animate a div to fly across the page, am I going to have to write lots of low level code to do that? If that's the case, and development time suffers to eek out that extra bit of performance, I can't see this as having much utility outside of niche fields like game development.
I don't mean to be overly critical mind as I'm still (and probably will always be) a bit of a n00b. I'd love it if somebody would point me at some resources that I could burn a weekend on, if I thought the juice was worth the squeeze. I'm just not sure I know enough to know if this is something that I could be productive in (some day).
It is very early days. More to come...
> However, I don't see a lot of the things that are in libraries like Vue and React
Yes. There's not too many of these yet; there are some though. For example, https://github.com/DenisKolodin/yew
There's also the inverse: can we re-write parts of libraries like Vue or React in something that compiles to wasm, so that you get better performance as the user? I know of at least one major SPA framework that's doing this. Don't want to spill the beans too much, even though it is technically public knowledge.
That said, that's how I think wasm will impact the lives of most JS programmers: as technology that underpins the libraries they use to make them better. Unless you want to, or unless you want to write a high-performance library, I wouldn't expect wasm to really change the way most JS programmers operate. It's abut augmenting JS, not replacing it.
> if I want to animate a div to fly across the page, am I going to have to write lots of low level code to do that?
That depends entirely on the library!
> I'd love it if somebody would point me at some resources that I could burn a weekend on
They're sorta scattered all over the place right now. Such is life for early adopters. More will come as stuff matures. Don't under-estimate how much this will change as the tooling gets built out, for example.
For React, we would love to do this although it's not clear what parts of React would benefit from being moved to wasm right now.
Very few of these changes are VM specific or likely to change (or any more so than WASM implementations)
- choose your algorithm carefully - make sure you’re paying attention to the data you’re applying the algorithm to is a good fit - pay attention to arity & GC pressure.
None of these are hard to do in JS & most of the VM debugging was to help identify problems in existing unoptimized code.
The rest are lessons you can take into ANY JS data processing.
(And if you're shipping so much JavaScript to your site users that you need to "minify", maybe you're doing it wrong.)
Your statement in brackets seems very "those damn kids"-ish. You need to "minify" your JS files even if you're shipping a small amount of JS, because loading performance matters and JS minifies very well.
If the source map decoder is slow, then the debugger feels slow.
100% agree.