Live data from Hacker News

Maybe you don't need Rust and WASM to speed up your JS

mrale.ph

171–180 of 186 posts

Re: Maybe you don't need Rust and WASM to speed up your JS

#171
post #25

I think this is an interesting exploration because I really enjoyed the description of profiling and improving performance, but I came away feeling exactly the opposite of the title. I think it's really cool that JS optimization can provide so many wins, but this article makes it seem fairly fickle and if they're not familiar with VM internals I would not expect most developers to complete this journey. Using wasm+ru…

And yet, which is better; finding someone that has the skill and knowledge needed to optimize this (e.g. bring in a consultant), or rewrite the thing - hundreds if not thousands of lines of code and working hours being replaced by even more. And not just rewrite it, but (IMO) replacing it with more complicated technology (additional steps required to make it usable, additional skills needed to work on the project). I…

Like any situation in software, there are likely to be tradeoffs. I certainly wouldn't advocate for launching large rewrites except under extreme duress. That said, I'm not sure I agree with how you're characterizing the tradeoffs for using wasm here. For one thing, the library in question sounds like it is a few thousand lines, not some gargantuan project. For another, part of what's awesome about wasm is that it can be somewhat incrementally adopted inside an existing project. I would also point out that for many teams or projects maintaining a consultant's code is not going to be as cost effective in the long run, especially if that code has a lot of language or VM specific optimizationswork done to it. Part of my point here is that the addition of new tech (rust, wasm) isn't complicated where optimizing JS is simple, but that they both carry complexity cost and should be assessed in context as possible solutions to a performance problem.

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.

Re: Maybe you don't need Rust and WASM to speed up your JS

#172

The problem with these tricks is that they're dependant on V8 internals, which means that there's no guarantee that it will be fast on Firefox (or edge, or even Safari). 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 th…

Even x86 machine code will work differently on different processors, because it's not executed directly but translated on another machine code and this translation is different on different processors (and AFAIK completely proprietary). There are layers above layers. I won't be surprised if someone will implement JVM over WASM to run Java applications in the browser. And JVM has JavaScript engine, so...

Re: Maybe you don't need Rust and WASM to speed up your JS

#173
post #154

Earlier quoted context omitted.

JS engines already do insane levels of optimization, and they do it while watching the code execute so they understand the code better than any preprocessing tool can hope to. What could a tool like you're describing do that the engines don't do themselves?

The author has proven exactly this point: By going through a number of engine-specific / implementation-specific code transformations they have achieved a significant performance boost for hot code, which, for whatever reason, the JS engines themselves failed to attain with the optimization repertoire they already have. Also, remember that JS engines are not all-powerful and all-knowing in their optimization techniqu…

> By going through a number of engine-specific / implementation-specific code transformations they have achieved a significant performance boost for hot code, which, for whatever reason, the JS engines themselves failed to attain with the optimization repertoire they already have.

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.

Re: Maybe you don't need Rust and WASM to speed up your JS

#174
post #155

Earlier quoted context omitted.

Expensive optimizations like what? Can you give a before/after example of something such a tool might do? (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.)

JIT engines usually don't do static analysis. I'm not sure if that is because the cost for that is that much higher, but a hint towards why could be that the engine simply does not know which parts of the (potentially huge amounts of) code that was loaded is actually going to be needed during execution, so analysing all of it is likely to bring more harm than gain. As an example for something that static analysis cou…

I replied in main to your other comment, but regarding the "Argument Adaptation" issue it's maybe worth noting that I'm 90% sure V8 would have optimized this automatically if not for the subsequent issue (with monomorphism). I'm dubious that the former issue could be statically fixed as easily as you suggest, but either way I think it should be considered a symptom of the latter issue.

Re: Maybe you don't need Rust and WASM to speed up your JS

#175
post #168

Earlier quoted context omitted.

Yes, this is because the JS spec requires that object keys are iterated in insertion order (with a bizarre exception for arrays).

Wait what? Since when? That wasn’t the case at all 5 or 6 years ago (I got bitten in the arse by Rhino not implementing it that way)

Most browsers already did, and some (not all) of the ES2015 stuff requires property ordering. Probably easier to just keep everything ordered since it could be needed by those methods.

Re: Maybe you don't need Rust and WASM to speed up your JS

#176

Earlier quoted context omitted.

JS engines already do insane levels of optimization, and they do it while watching the code execute so they understand the code better than any preprocessing tool can hope to. What could a tool like you're describing do that the engines don't do themselves?

Well, for one, the compiler could rearrange key assignment of similarly shaped objects, so that they actually are similarly shaped objects to the JIT .... but that seems like really dangerous territory

Something like that would probably speed up more code than it breaks, but it would be a breaking change, so probably not what could be strictly considered optimization.

Re: Maybe you don't need Rust and WASM to speed up your JS

#177

Earlier quoted context omitted.

> My understanding is that "tagged unions" and "sum types" are the same thing Well, your understanding is wrong. Sum types satisfy a specific universal property, which union types don't.

What specific universal property?

The universal property for a sum type that having an X-valued function from a sum is equivalent to having X-valued functions from each summand, for any type X. In other words:

(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.

Re: Maybe you don't need Rust and WASM to speed up your JS

#178
post #79
post #4

Source maps are a debug tool. Why does the performance matter? (And if you're shipping so much JavaScript to your site users that you need to "minify", maybe you're doing it wrong.)

> if you're shipping so much JavaScript to your site users that you need to "minify", maybe you're doing it wrong. 100% agree.

Make that 101% agree.

Re: Maybe you don't need Rust and WASM to speed up your JS

#179

Earlier quoted context omitted.

What specific universal property?

The universal property for a sum type that having an X-valued function from a sum is equivalent to having X-valued functions from each summand, for any type X. In other words: (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…

Interesting, thanks for the explanation. 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. Seems like maybe this is all just a clash between formalism and how languages work in practice, similar to how it's nice to just pretend that computers work on real numbers instead of floats. In this case, maybe you'd need to pretend that JS works on values rather than having object identity.

> 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%...

Re: Maybe you don't need Rust and WASM to speed up your JS

#180

Earlier quoted context omitted.

The universal property for a sum type that having an X-valued function from a sum is equivalent to having X-valued functions from each summand, for any type X. In other words: (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…

Interesting, thanks for the explanation. 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. Seems like maybe this is all just a clash between formalism and how languages work in practice, similar to how it's nice to just pretend that computers work on real numbers instead of floats. In this case, maybe you'd need to pretend that JS works o…

> Seems like maybe this is all just a clash between formalism and how languages work in practice,

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:

(f%3A%20(s%3A%20Sum)%20%3D>%20X)%3A%20%5B(a%3A%20A)%20%3D>%20X%2C%20(b%3A%20B)%20%3D>%20X%5D%20%7B%0D%0A%20%20return%20%5B%0D%0A%20%20%20%20(a%3A%20A)%3A%20X%20%3D>%20f(%7Btype%3A%20'A'%2C%20a%7D)%2C%0D%0A%20%20%20%20(b%3A%20B)%3A%20X%20%3D>%20f(%7Btype%3A%20'B'%2C%20b%7D)%2C%0D%0A%20%20%5D%3B%0D%0A%7D%0D%0A%0D%0Afunction%20bar(fa%3A%20(a%3A%20A)%20%3D>%20X%2C%20fb%3A%20(b%3A%20B)%20%3D>%20X)%3A%20(s%3A%20Sum)%20%3D>%20X%20%7B%0D%0A%20%20return%20(s%3A%20Sum)%3A%20X%20%3D>%20%7B%0D%0A%20%20%20%20if%20(s.type%20%3D%3D%3D%20'A')%20%7B%0D%0A%20%20%20%20%20%20return%20fa(s.a)%3B%0D%0A%20%20%20%20%7D%20else%20%7B%0D%0A%20%20%20%20%20%20return%20fb(s.b)%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%7D%3B%0D%0A%7D%0D%0A%0D%0Alet%20a%3A%20Sum%20%3D%20%7B%20type%3A%20'A'%2C%20a%3A%20%7B%20numValue%3A%2042%20%7D%20%7D%3B%0D%0Alet%20b%3A%20Sum%20%3D%20%7B%20type%3A%20'B'%2C%20b%3A%20%7B%20strValue%3A%20"hello%20world!"%20%7D%20%7D%3B%0D%0A%0D%0Afunction%20original(s%3A%20Sum)%3A%20boolean%20%7B%0D%0A%20%20return%20(s%20%3D%3D%20a%20%7C%7C%20s%20%3D%3D%20b)%3B%0D%0A%7D%0D%0A%0D%0Alet%20%5Bon_a%2C%20on_b%5D%20%3D%20foo(original)%3B%0D%0Alet%20reconstructed%20%3D%20bar(on_a%2C%20on_b)%3B%0D%0A%0D%0Aalert(original(a))%3B%0D%0Aalert(reconstructed(a))%3B" rel="nofollow">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

Post reply on HN