134 ms·
My own experience is that JS can be really, really fast if you manage to avoid confusing the JIT. But it's also very easy to make stuff slow and it can be hard
by fabian2k 4y ago
My own experience is that JS can be really, really fast if you manage to avoid confusing the JIT. But it's also very easy to make stuff slow and it can be hard to guess what kind of stuff will be an order of magnitude slower because it avoids a fast path.
- dottedmag 4y agoAnd that's why profiling is the step 1 for any optimization.
- maccard 4y agoWhich is why this article is so good IMO.
- Yahivin 4y agoIn other words: JavaScript is fast, JavaScript developers are slow.
- hajile 4y agoMore like JS doesn't do anything to discourage writing slow code. Typescript should have been that tool IMO, but instead, it is so flexible that it doesn't do a thing to help developers understand that they are writing slow code.
- klysm 4y agoI appreciate typescripts stance to be a non-optimizing compiler. Having two optimization layers would make things weird
- The_Colonel 4y agoI don't think that's what the other commenter suggested. The point is that TypeScript should disallow (or at least make harder) to write code which is hard to optimize by the VM - e.g. allowing function parameters to be of multiple types. But TS has a design constraint to be essentially a superset of JavaScript so it would be difficult to realize this goal.
- k__ 4y agoI had the impression, my code gets less polymorphic when I use TypeScript.
- hajile 4y agoIf only. This is polymorphic because union types are polymorphic. let stringProcessor = (foo: string | number): string => { //do stuff with foo return "string" + foo } It's super easy to make non-optimized arrays and objects this way. type MyFoo = string | number //somewhere in the codebase //you will get zero hints that this is a polymorphic function let formatFoos = (foos: MyFoo[]) => foos.map((foo) => `${foo} deoptimized`)) Any use of optional object properties also immediately leads to polymorphism too. I doubt you can find ANY significant TS codebase where this isn't used pervasively. interface Foo { foo: MyFoo //polymorphic bar: string baz?: number //this is polymorphic too } let doStuff = (obj: Foo): string { //do stuff with obj return "string" } doStuff({bar: "abc"}) doStuff({bar: "abc", baz: 123}) //we're now polymorphic Even if we remove the `?`, it can still be polymorphic because the TS compiler doesn't reorder object properties when compiling. interface Foo { bar: string //all basic types, so we're good? baz: number //no more optional, so we're fine right? } let doStuff = (obj: Foo): string { //do stuff with obj return "string" } doStuff({baz: 123, bar: "abc"}) doStuff({bar: "abc", baz: 123}) //we changed order, so now we're now polymorphic
- brundolf 4y agoI'm very surprised the property ordering would matter. Seems like the JS engine itself could easily fix that, no? Silently re-order the properties to fit its own optimization Do you have a source for the above info? (both because I'm a little skeptical of some parts of it, but also because I'd love to learn more about this topic and I've had trouble finding concrete info in the past)
- hajile 4y agoJS specifies that the order keys are added makes a difference. For this reason, entering them in two different ways is a fundamentally different, non-interchangeable object type.
- croes 4y agoJust because it can be faster doesn't mean it's fast.
- hajile 4y agoWhen you get down to basics, JS has arrays, structs, ints, floats, and strings+regex (both backed by very fast C++ implementations. If you stick to these things as if they were C analogs, the performance potential is very high. Viewed in those terms, it shouldn't be surprising that JS can be incredibly fast (faster than C in some cases).
- maccard 4y agoNo, it's possible to write dodgy code in other languages too - 2 of the examples in this article are in go rather than JS
- deleted 4y ago[deleted]
- cxr 4y ago> But it's also very easy to make stuff slow and it can be hard to guess what kind of stuff will be an order of magnitude slower It's not that hard to guess. Just look at what most NPM/Electron programmers and guides recommend you do, and then do the opposite. I.e., think about what your program is doing, and write the most boring, 90s-style code you can. Works really well internally for the Firefox codebase, and has been for 20+ years (for longer than it has even been called "Firefox"). Relevant talk: <https://www.youtube.com/watch?v=p-iiEDtpy6I https://www.youtube.com/watch?v=p-iiEDtpy6I> Relevant paper: <https://users.dcc.uchile.cl/~rrobbes/p/EMSE-features.pdf https://users.dcc.uchile.cl/~rrobbes/p/EMSE-features.pdf> Relevant thread: <https://news.ycombinator.com/item?id=33790568 https://news.ycombinator.com/item?id=33790568>
- hajile 4y agoFor those who don't want to go through all that, there's basically 3 simple rules to make your code optimizable: * Objects should have a fixed set of keys (no adding more and especially no `delete`) and the type of the value should never change (note: in JS, a type must NOT be a union of multiple types if you want it to optimize) * Arrays must be of one type and a fixed length * Functions must take a fixed number of arguments and the type of those arguments must never change. This is by far the most important if you want to get your function's code optimized. And as a corollary from this article, it's not JS specific, but converting string -> float and float -> string isn't a cheap operation. I'd go so far as to say that it is the most expensive operation in a huge amount of the functions where it happens. Int to string is especially egregious because it requires a lot of division which is basically the slowest (common) thing you can do on a CPU.
- esperent 4y ago> Arrays must be of one type and a fixed length If performance is critical then use a TypedArray where possible. Your other points are spot on for high performance JS.
- crabmusket 4y agoI was messing about with some profiling and seemed to find that TypedArrays are not always faster than regular arrays carefully handled. And what's worse it seemed like a regression from node 16 (fast typed arrays) to 18 (slow typed arrays). I will have to dig up my profiles again and see if I can verify it. Posting this comment in the hope that someone who knows more than me has something to add!
- AtlasBarfed 4y ago"really, really fast" Compared to what? Polar bears? Here: is it faster than warm JVM?
- winrid 4y agoV8 is almost never faster than a warm JVM, especially once you get to heap sizes bigger than a couple GB. You can't even store more than 14m or so keys in a map in Node w/o reaching for an off heap solution due to hard limits in the runtime...
- fabiospampinato 4y agoIf needed you can make your own map that doesn't let you down: https://www.npmjs.com/package/infinity-map https://www.npmjs.com/package/infinity-map
- winrid 4y agoI've used megahash with great success: https://github.com/jhuckaby/megahash https://github.com/jhuckaby/megahash
- ptrwis 4y agoI would freeze before the JVM would warm up.
- winrid 4y agoJokes aside, then maybe your code cache size is too small ;p
- mhagemeister 4y agoAuthor here. True, optimize code for JS engines is certainly an art form of its own. What usually works for me is to write boring imperative code when performance is a requirement. That seems to be easily optimizable by current engines. Also, thanks for sharing my article here on HN. First time for me one of them is on HN.
- avh3 4y agoI enjoyed reading your blogpost. Thanks for sharing!