News for hackers. Stuff that matters.
Re:Speeding up the JavaScript ecosystem – one library at a time by fabian2k on Tuesday November 29, 2022 @04:40PM (#33793716)
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.
Re:Speeding up the JavaScript ecosystem – one library at a time by dottedmag on Tuesday November 29, 2022 @04:45PM (#33793784)
And that's why profiling is the step 1 for any optimization.
Re:Speeding up the JavaScript ecosystem – one library at a time by maccard on Wednesday November 30, 2022 @05:27AM (#33798990)
Which is why this article is so good IMO.
Re:Speeding up the JavaScript ecosystem – one library at a time by Yahivin on Tuesday November 29, 2022 @04:53PM (#33793889)
In other words: JavaScript is fast, JavaScript developers are slow.
Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Tuesday November 29, 2022 @08:52PM (#33795938)
More 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.
Re:Speeding up the JavaScript ecosystem – one library at a time by klysm on Tuesday November 29, 2022 @11:34PM (#33797065)
I appreciate typescripts stance to be a non-optimizing compiler. Having two optimization layers would make things weird
Re:Speeding up the JavaScript ecosystem – one library at a time by The_Colonel on Wednesday November 30, 2022 @03:58AM (#33798442)
I 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.

Re:Speeding up the JavaScript ecosystem – one library at a time by k__ on Wednesday November 30, 2022 @06:19AM (#33799274)
I had the impression, my code gets less polymorphic when I use TypeScript.
Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Wednesday November 30, 2022 @09:17AM (#33801053)
If 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
Re:Speeding up the JavaScript ecosystem – one library at a time by brundolf on Wednesday November 30, 2022 @01:34PM (#33805356)
I'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)

Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Wednesday November 30, 2022 @06:00PM (#33809249)
JS 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.
Re:Speeding up the JavaScript ecosystem – one library at a time by brundolf on Wednesday November 30, 2022 @11:53PM (#33812493)
You could preserve that information and still have the same native struct, since it only becomes relevant when iterating the keys
Re:Speeding up the JavaScript ecosystem – one library at a time by klysm on Wednesday November 30, 2022 @09:42AM (#33801361)
This seems like a moving target though, and I still don’t think the typescript compiler should be responsible for it. I think a linter could make sense though for obviously bad cases.
Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Wednesday November 30, 2022 @01:11PM (#33804927)
A linter can't tell if it's polymorphic -- only a compiler which is actually analyzing the types.
Re:Speeding up the JavaScript ecosystem – one library at a time by croes on Wednesday November 30, 2022 @12:38AM (#33797373)
Just because it can be faster doesn't mean it's fast.
Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Wednesday November 30, 2022 @09:38AM (#33801319)
When 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).

Re:Speeding up the JavaScript ecosystem – one library at a time by maccard on Wednesday November 30, 2022 @05:28AM (#33798994)
No, it's possible to write dodgy code in other languages too - 2 of the examples in this article are in go rather than JS
Re:Speeding up the JavaScript ecosystem – one library at a time by tonto on Wednesday November 30, 2022 @06:30AM (#33799338)
really? the article looks all js
Re:Speeding up the JavaScript ecosystem – one library at a time by cxr on Tuesday November 29, 2022 @06:16PM (#33794755)
> 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>

Relevant paper: <https://users.dcc.uchile.cl/~rrobbes/p/EMSE-features.pdf>

Relevant thread: <https://news.ycombinator.com/item?id=33790568>

Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Tuesday November 29, 2022 @09:06PM (#33796032)
For 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.

Re:Speeding up the JavaScript ecosystem – one library at a time by esperent on Tuesday November 29, 2022 @09:59PM (#33796474)
> 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.

Re:Speeding up the JavaScript ecosystem – one library at a time by crabmusket on Wednesday November 30, 2022 @07:29AM (#33799755)
I 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!
Re:Speeding up the JavaScript ecosystem – one library at a time by meheleventyone on Wednesday November 30, 2022 @02:04AM (#33797802)
> Objects should have a fixed set of keys.

Declared in a fixed order if you want functions to remain monomorphic! One case where using classes is a bit safer than object literals.

Re:Speeding up the JavaScript ecosystem – one library at a time by hajile on Wednesday November 30, 2022 @08:58AM (#33800804)
It's important, but I purposely stepped around this to not complicate things.

In truth, the only likely situation from these rules is hand-copying configuration then manually transposing. This isn't likely to happen in really hot code paths.

This is the one place where JS helps us because lazy typers are encouraged to return object literals from factory functions and the return will almost always be one object returned at the bottom of the function.

It also (intentionaly) bypasses manually adding elements to an object directly after the object is instantiated. In truth, doing this consistently and directly after it is created can usually be optimized too, but not necessarily and describing this also complicates things (and varies from one JIT to the next).

Re:Speeding up the JavaScript ecosystem – one library at a time by samus on Wednesday November 30, 2022 @02:05AM (#33797806)
> in JS, a type must NOT be a union of multiple types if you want it to optimize)

Does that mean that tagged unions (functional programming-style data types) are inherently slow in JavaScript? Maybe there is a way for the TypeScript compiler to pass pragmas (via comments in generated code) to the interpreter.

Re:Speeding up the JavaScript ecosystem – one library at a time by meheleventyone on Wednesday November 30, 2022 @03:17AM (#33798256)
They can have pitfalls is a better way to think about it. Inheritance has a similar penalty.

The potential problem is that JS engines only see the ‘shape’ of an object. That is the members and member order. Which is slightly more strict that TS’s structural types where order doesn’t matter. So union variants and subtypes are each different shapes if they have different members or member order.

Shapes are important for function calls. Objects property access is optimised based on the number of ‘shapes’ that have been seen. Into the following:

Monomorphic - It’s seen only 1 shape and can access the properties by offset directly.

Polymorphic - A small inline cache of several shapes. Access is a lookup into this. Slightly slower.

Megamorphic - A lookup into a global table. This is a performance cliff.

So if you have lots of functions that take a tagged union or base class *and at runtime* they see lots of different types passed in *and this is in a hot path* they can be a problem.

Re:Speeding up the JavaScript ecosystem – one library at a time by samus on Wednesday November 30, 2022 @06:21PM (#33809496)
Thanks for the explanation!
Re:Speeding up the JavaScript ecosystem – one library at a time by pjc50 on Wednesday November 30, 2022 @05:35AM (#33799030)
> 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.

Yeah. There's some great benchmarking and tips: https://www.zverovich.net/2013/09/07/integer-to-string-conve...

Godbolt example: https://godbolt.org/z/M4b353PKv of which the meat is

        imul    rcx, rcx, 1374389535
        shr     rcx, 37
        imul    edi, ecx, 100
        mov     r8d, edx
        sub     r8d, edi
        movzx   edi, WORD PTR .LC2[r8+r8]
        mov     WORD PTR [rsi], di
So if our example input is 12345, the first two instructions use the field-inverse property to compute "123" with multiply rather than divide (1 clock, latency 4), then multiply up again to get "12300", then subtract that to get "45". That can then be looked up at position 45+45 in the string "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899" to give you the two digits.
Re:Speeding up the JavaScript ecosystem – one library at a time by jansan on Wednesday November 30, 2022 @06:41AM (#33799410)
It would be great if there was a tool that shows you where deoptimization is taking place. IIRC the Chrome Javascript profiler once flagged functions where optimization failed, but this feature is not available in current versions of the devtools performance tool. Although deoptimization is logged, I am not aware of a way to trace the entries back to the source code.
Re:Speeding up the JavaScript ecosystem – one library at a time by crabmusket on Wednesday November 30, 2022 @07:26AM (#33799733)
This makes me wish "use strong" had taken off, at least as a strict mode for development. Helping you stay within the JIT's happy path.
Re:Speeding up the JavaScript ecosystem – one library at a time by Kamq on Tuesday November 29, 2022 @10:43PM (#33796786)
> and write the most boring, 90s-style code you can

I think this advice goes way back to before the 90s. I've also heard it called FIAL (Fortran In Any Language).

But, yeah. It's basically always the fastest thing possible in anything even approaching the mainstream.

Re:Speeding up the JavaScript ecosystem – one library at a time by AtlasBarfed on Tuesday November 29, 2022 @07:43PM (#33795417)
"really, really fast"

Compared to what? Polar bears?

Here: is it faster than warm JVM?

Re:Speeding up the JavaScript ecosystem – one library at a time by winrid on Tuesday November 29, 2022 @08:01PM (#33795554)
V8 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...

Re:Speeding up the JavaScript ecosystem – one library at a time by fabiospampinato on Tuesday November 29, 2022 @11:44PM (#33797102)
If needed you can make your own map that doesn't let you down: https://www.npmjs.com/package/infinity-map
Re:Speeding up the JavaScript ecosystem – one library at a time by winrid on Wednesday November 30, 2022 @01:11AM (#33797548)
I've used megahash with great success:

https://github.com/jhuckaby/megahash

Re:Speeding up the JavaScript ecosystem – one library at a time by ptrwis on Tuesday November 29, 2022 @08:27PM (#33795757)
I would freeze before the JVM would warm up.
Re:Speeding up the JavaScript ecosystem – one library at a time by winrid on Wednesday November 30, 2022 @01:12AM (#33797555)
Jokes aside, then maybe your code cache size is too small ;p
Re:Speeding up the JavaScript ecosystem – one library at a time by mhagemeister on Wednesday November 30, 2022 @02:15AM (#33797871)
Author 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.

Re:Speeding up the JavaScript ecosystem – one library at a time by avh3 on Wednesday November 30, 2022 @03:22AM (#33798283)
I enjoyed reading your blogpost. Thanks for sharing!