[HN Gopher] How JavaScript engines achieve great performance
       ___________________________________________________________________
        
       How JavaScript engines achieve great performance
        
       Author : EntICOnc
       Score  : 63 points
       Date   : 2021-11-24 07:00 UTC (16 hours ago)
        
 (HTM) web link (blogg.bekk.no)
 (TXT) w3m dump (blogg.bekk.no)
        
       | Skinney wrote:
       | This article is a part of a larger series that tries to figure
       | out how Elm's runtime performance can be improved:
       | https://blogg.bekk.no/successes-and-failures-in-optimizing-e...
       | 
       | I'm the author of the articles, btw.
        
         | PragmaticPulp wrote:
         | Huge thanks for writing all of this up. This is great content.
        
       | btilly wrote:
       | I still think that http://steve-
       | yegge.blogspot.com/2008/05/dynamic-languages-st... is one of the
       | best overviews ever of how JavaScript managed to become as fast
       | as traditional compiled languages despite being filled with
       | features to make it slow.
       | 
       | What's amazing is that that talk came out several months before
       | V8, the first JavaScript engine that actually used the ideas in
       | that talk. But, of course, that talk was possible because V8 was
       | already under development.
        
         | bestouff wrote:
         | In my experience JS is not as fast as compiled languages.
        
           | nicoburns wrote:
           | It's not, but it's _much_ closer than other dynamic languages
           | like Python, PHP, Ruby, etc. For numerical code (which avoids
           | allocation) it 's often pretty close.
        
             | hu3 wrote:
             | Microbenchmarks I know, but it looks like a magnitude
             | slower than C++ to me:
             | 
             | https://benchmarksgame-
             | team.pages.debian.net/benchmarksgame/...
        
               | nicoburns wrote:
               | I see a factor of 2 in the closest benchmarks. A factor
               | of 4-5 for the middlish ones, and a factor of 10 for the
               | least close. But it's worth noting that a lot of the
               | those C++ examples are making heavy use of SIMD.
        
               | netr0ute wrote:
               | Can you do SIMD with JS? If not, then C++ has the real
               | advantage.
        
         | Zababa wrote:
         | > as fast as traditional compiled languages
         | 
         | I don't think JS is as fast as traditional compiled languages.
         | Java/C# are faster, Go is faster, OCaml/Haskell are faster,
         | Common Lisp is faster, C/C++/Rust are faster.
        
         | IainIreland wrote:
         | Eh, with the benefit of 14 years of hindsight, I want to push
         | back on some of the things in that talk. (Context: I work on
         | SpiderMonkey.)
         | 
         | First, all the stuff about tracing and trace trees is kind of
         | obsolete. SpiderMonkey abandoned TraceMonkey a long time ago.
         | (To the best of my knowledge, V8 never implemented a tracing
         | JIT at all.) The problem with tracing is that you can get
         | really good performance when everything goes right, but it's
         | brittle. There's a reference in the talk to how implementations
         | of the Game of Life can have exponential blow-up, for example.
         | You can usually fix any individual pathological case, but the
         | inherently exponential number of possible paths through a
         | program make it difficult to completely eliminate weird
         | performance cliffs.
         | 
         | If your goal is to maximize performance on a known set of
         | benchmarks, go wild. If you want a robust engine that can
         | handle whatever weird code the web is throwing at you, tracing
         | JITs are (as far as I can tell) a dead end.
         | 
         | (Counterpoint: LuaJIT seems to be doing alright with tracing,
         | although it may just solve the problem by punting to the
         | programmer: https://github.com/lukego/blog/issues/29. That's
         | more feasible when you don't have multiple engines with
         | performance cliffs in subtly different places.)
         | 
         | Second, the idea that JIT-compiled code can be _faster_ than
         | AOT-compiled code has been floating around for a long time, but
         | I don 't think it really holds in the general case. Doing work
         | at runtime isn't free: not just time spent compiling, but also
         | time spent profiling and validating that your speculative
         | optimizations continue to be correct.
         | 
         | SpiderMonkey had a top-tier optimizing compiler, IonMonkey,
         | that got pretty darn close to native code on hot benchmark
         | loops. We tracked whole-program information to ensure that type
         | checks could be elided in inner loops. (For example, if the `x`
         | property of a certain set of objects only ever contained 32-bit
         | integers, then we could unbox it without checking the type. If
         | any code elsewhere ever stored a non-integer value in that
         | property, we would notice and invalidate the optimized code.)
         | 
         | We threw IonMonkey away, because it was too brittle. In
         | practice, real-world code falls off the happy path often enough
         | that we got better performance by accepting that even your
         | highly optimized JS code will include some runtime checks.
         | Invalidation and recompilation are real costs. So is the upkeep
         | of all the global data necessary to support Ion. There's an
         | engineering tradeoff between pushing up the performance ceiling
         | and bringing up the performance floor; we've been happy with
         | our choice to shift focus more towards the latter. Our numbers
         | are down on artificial benchmarks, but it seems to have paid
         | off in real-world performance. (Also, bugs in the optimizing
         | compiler are significantly less likely to be exploitable.)
         | 
         | A lot of really smart people have done some incredible work on
         | the JVM. Nevertheless, I'm still not aware of any code written
         | in Java instead of (say) C++ or Rust because Java was _faster_.
         | I think it 's more accurate to say that JIT compilation can be
         | _fast enough_ that the other advantages of the language can
         | make it the right choice.
        
       | nick_ wrote:
       | Every time I read about the insane achievements of a language
       | like JavaScript, I am reminded of how disappointing it was to
       | watch. Maturing JavaScript was a lateral move that cost, what, a
       | billion man-hours?
       | 
       | The new cross-plat runtimes and languages we could have had by
       | now if JavaScript didn't have a stranglehold on the available
       | browser-side language space... wow.
        
         | dntrkv wrote:
         | That's one way to look at it.
         | 
         | Another way is to appreciate the fact that we have an open-
         | platform with a standards body that actually works (queue the
         | responses about how it actually _doesn't_ work). I can write an
         | app once and access it from any device and it will work today,
         | and it will work 10 years from now.
         | 
         | No other platform has achieved what the web browsers have, so I
         | think we might be doing something right.
        
         | jimbob45 wrote:
         | In Eich's defense, I've read before that he originally wanted
         | to make a Scheme dialect instead of JS but was given the finger
         | by higher-ups (and only two weeks to create JS 1.0 to begin
         | with!).
        
           | hajile wrote:
           | Don't forget that the most complained about feature -- type
           | coercion -- was added later at the explicit request of
           | developers and then left in due to force from Microsoft.
        
             | dstroot wrote:
             | ..who then created Typescript
        
         | [deleted]
        
         | edave64 wrote:
         | > The new cross-plat runtimes and languages we could have had
         | by now if JavaScript didn't have a stranglehold on the
         | available browser-side language space
         | 
         | Probably barely working, super fragmented garbage. A standard
         | that isn't optimal but works well enough is fantasic in
         | comparison to everybody trying to make their own thing. And
         | realistically, if something else would have emerged, people
         | would whine about it just as much as they do about JS.
         | 
         | Nowadays, we can use pretty much any language we want and
         | compile it down to JS/WebASM. And most just add some compile
         | checks with TypeScript. Because that's good enough, and it
         | works.
        
       | ithrow wrote:
       | Javascript being fast as Java and coupled with Typescript the
       | future of Java doesn't look very bright.
        
         | nayuki wrote:
         | JavaScript is most definitely not as fast as Java. Try doing
         | some CPU- and memory-intensive Project Euler problems in both
         | languages and you'll see a several times difference.
         | 
         | Java has a much better standard library - abstract data type
         | interfaces, common and fast data structures, I/O facilities,
         | concurrency, and more.
        
           | romero-jk wrote:
           | True but for a dynamic lang is very close[0] and if your
           | application lives in the Java collection framework(most do)
           | instead of using arrays the performance is even closer.
           | 
           | [0] https://benchmarksgame-
           | team.pages.debian.net/benchmarksgame/...
        
         | tyingq wrote:
         | >Javascript being fast as Java
         | 
         | Perhaps for some specific use cases, but certainly not for
         | others. I also suspect moving from a fairly broad "stdlib" and
         | somewhat curated 3rd party packages to the wild west of npm
         | might be a barrier for many.
        
         | kaba0 wrote:
         | Well, let's just add that TruffleJS, a js implementation in
         | java which is part of the Graal project (which is a java
         | interpreter written in java, running on top of java) can
         | achieve comparable speeds to V8 for long running tasks (java's
         | JIT compilers "turn on" later than js's because one has to be
         | quite fast as soon as possible while the other has to be really
         | fast but can warm up a bit more).
         | 
         | There is a slight trick here, because this java interpreter
         | uses a few special JIT optimizations, but it is still 100% java
         | code (and the clever reuse of the engineering marvel what the
         | JVM is). (Also, do check out the Graal project, its AOT
         | compilation may be the least interesting part! It can even
         | inline python code into js and optimize them together!)
         | 
         | Also, Java will soon get virtual threads that will
         | automagically become non-blocking, even if they are written
         | with blocking code (similarly to go). Currently in incubator
         | mode is the Vector API which let's people write really low
         | level SIMD code that can cleverly query the processors
         | capabilities and will even safely fallback to for loops on
         | ineligible CPUs. FFI will greatly improve also with project
         | Panama, so libs like tensorflow can get better integration into
         | the JVM.
         | 
         | And last but not least, Valhalla is coming with value types.
         | Yeah, it won't happen for a few years still, but once it does,
         | there will hardly be a platform as performant as the JVM. So,
         | while we have heard it plenty of times how java will soon die,
         | I think it's future is brighter than ever.
        
         | ragnese wrote:
         | TypeScript is awful. Like, I shit on Java all the time, and I
         | WAY rather work in Java than TypeScript.
         | 
         | TypeScript's type system is so utterly broken that I honestly
         | don't know if my code is ANY more robust than if I had written
         | it in JavaScript.
         | 
         | Record<> is broken/unsound:
         | https://github.com/microsoft/TypeScript/issues/45335
         | 
         | Generics are wonky and sometimes wrong:
         | https://github.com/microsoft/TypeScript/issues/31006
         | 
         | The `readonly` keyword does absolutely nothing:
         | https://github.com/microsoft/TypeScript/issues/13347
         | 
         | Arrays are covariant in their type param, so I can pass a
         | `Dog[]` into a function that accepts `Animal[]`. If that
         | function adds a `Cat` to the passed array, the compiler is
         | perfectly happy, but we'll see a runtime error.
         | 
         | TypeScript is actually so bad that it might have honestly made
         | JavaScript _worse_ , if that's even possible.
        
           | quaunaut wrote:
           | I generally agree with everything you're saying, after
           | extensive use of Typescript. Its safety guarantees usually
           | only help within extremely tight bounds, and there's
           | surprisingly little that it catches that couldn't be inferred
           | directly.
           | 
           | However, a question:
           | 
           | > Arrays are covariant in their type param, so I can pass a
           | `Dog[]` into a function that accepts `Animal[]`. If that
           | function adds a `Cat` to the passed array, the compiler is
           | perfectly happy, but we'll see a runtime error.
           | 
           | I don't think this one is true anymore, assuming you type it
           | correctly. I've provided an example[1].
           | 
           | ---
           | 
           | Separately however, I think it's worth noting that I've been
           | playing with the idea of doing a project in plain Javascript
           | again, to see if I feel any serious productivity losses.
           | 
           | - [1]: https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg
           | hsKBeK...
        
           | Spivak wrote:
           | This seems like such an odd complaint because TS is very
           | loudly purposely unsound. So much of TS is purposely unsound.
           | Like unless you turn on an option disabling it function
           | parameters are contravariant! If you write a function that
           | takes a Dog you can pass an Animal and it will type check!
        
           | Zababa wrote:
           | > TypeScript's type system is so utterly broken that I
           | honestly don't know if my code is ANY more robust than if I
           | had written it in JavaScript.
           | 
           | You gave three examples that seem relatively rare. At work we
           | use TypeScript to type a codebase that was mostly plain
           | JavaScript, and we write TypeScript as plain JavaScript. It
           | does very well its job of offering use better tooling and
           | compile-time guarantees. If you consider it as a way to
           | statically-type existing JS codebases, it's good. If you
           | consider it as a language on its own, it's not great.
        
           | phailhaus wrote:
           | Wild how people can have such different experiences.
           | Typescript is hands-down one of my favorite languages, and I
           | would choose its structural type system over Java's any day.
           | I also use Python at work, and it's like night and day in
           | terms of quality. Every day I have to deal with fundamentally
           | unresolvable issues due to Python's half-baked type hints,
           | when I could be writing much more stable, functional, and
           | maintainable Typescript.
           | 
           | Calling it "broken" is complete hyperbole, and no, it's not
           | "worse than Javascript". There's a reason why the entire
           | ecosystem is switching to Typescript, and that's because it
           | is extremely effective at helping teams manage complex
           | stateful applications.
        
           | brundolf wrote:
           | It has holes, mainly because it has to be able to cover
           | existing JavaScript codebases, which for many reasons can be
           | an order of magnitude harder to type than code that's written
           | from the beginning to be statically typed.
           | 
           | The biggest holes can be filled via compiler options, and
           | most of the rest can be papered over with the right coding
           | practices (use Maps instead of Records, lint against `any`
           | and type-casting, etc).
           | 
           | I haven't encountered the situation you described, but you
           | could probably solve it by making the function generic over
           | TArray extends Animal[].
           | 
           | The rest of what you've said doesn't line up with my
           | experience. TypeScript is an imperfect tool that requires
           | some elbow-grease to fully benefit from. I would prefer a
           | typed language that didn't have to work under its real-world
           | constraints (but not Java, because of null-checking at the
           | very least), but over the years it's caught more bugs for me
           | than I could possibly hope to count.
        
           | skitter wrote:
           | > Arrays are covariant in their type param, so I can pass a
           | `Dog[]` into a function that accepts `Animal[]`. If that
           | function adds a `Cat` to the passed array, the compiler is
           | perfectly happy, but we'll see a runtime error.
           | 
           | The same is true for Java arrays. I thought the Liskov
           | substitution principle was tautological when I first heard
           | about it, but it seems like the original designers of Java
           | disagree. Another example (albeit from the standart library,
           | not the typesystem itself) is that if you try to modify a
           | `List` or similar collection, it may throw an
           | `UnsupportedOperationException` because it's immutable -
           | instead of having a superinterface with only non-mutating
           | methods and only implementing that.
        
           | Scarbutt wrote:
           | It's a shame that something like Typescript is and will
           | dominate the static typing JS ecosystem/industry instead of
           | something simpler and sane like Rescript.
        
             | ronenlh wrote:
             | Rescript is sane but I wouldn't all it simpler. It can be
             | really hard to do trivial stuff with it. I felt I had to
             | study OCaml to understand it.
        
               | quaunaut wrote:
               | I think it gets pretty close, but admittedly its handling
               | of unknown objects is just downright bad.
               | 
               | Also, somehow, the tooling for it has one of the best
               | features around(compiling to readable JS), but all other
               | tooling is completely absent, and frankly, bad.
        
           | [deleted]
        
         | kvathupo wrote:
         | You can't create lock-free data structures in Javascript (as of
         | this comment), but you can do so in Java.
        
         | jetsetgo wrote:
         | Please stop. These kind of remarks just brings the worst of out
         | community. JAVA is and will be fine. It's doing better than
         | ever.
        
       | stephc_int13 wrote:
       | Is Javascript considered to be fast?
       | 
       | It might be close to as fast as it could given the inherent
       | constraints of the language, but I would not call it fast in a
       | general way.
        
         | servytor wrote:
         | Javascript is actually quite fast, on par with Java[0], but it
         | is DOM manipulation that makes it seem so slow.
         | 
         | [0]: https://benchmarksgame-
         | team.pages.debian.net/benchmarksgame/...
        
         | pantsforbirds wrote:
         | I mean there are a number of benchmarks where Node outperforms
         | java if you look at the benchmark comparison:
         | https://benchmarksgame-
         | team.pages.debian.net/benchmarksgame/.... The optimization work
         | that goes into JS is actually really impressive and comes from
         | most of the biggest tech companies.
        
         | irrational wrote:
         | I've been using JavaScript since 1997. The fact that you can
         | ask this question makes me think you weren't using JS in the
         | first decade of the WWW. Frankly until Google came in and shook
         | things up, JS was incredibly slow. Think 14.4k modem versus 1
         | gigabit fiber optic speed difference.
        
         | kaba0 wrote:
         | Yes, it is a quite fast for a managed language. For comparison,
         | python is roughly 10x slower than it.
        
       | xg15 wrote:
       | I wonder if the browser cache could be used to effectively speed
       | up compilation.
       | 
       | I'd imagine that most scripts in websites are static assets, i.e.
       | change rarely and have cache headers that support storage.
       | Couldn't you make use of that to save a compiled version of the
       | script together with the cached file? So when the page is loaded
       | again, scripts are already compiled.
       | 
       | Is something like this done in practice?
        
         | onion2k wrote:
         | All browsers have JS byte code caching, but in my experience
         | building web apps it doesn't make a huge difference in the real
         | world. A user will hit a site, download and compile the JS
         | bundle, and then that tab will be kept open for a day or two.
         | They might close the tab and reopen it the following day. In
         | any modern app that uses continuous deployment that means
         | they'll certainly be downloading a new client bundle, and a lot
         | of the time they'll also also get a new vendor bundle (client
         | bundle is the app code, vendor bundle is the NPM packages). I
         | have no doubt code could be split to optimize for fewest
         | changes but JS tooling is complicated enough already so I doubt
         | many sites would bother.
        
         | IainIreland wrote:
         | SpiderMonkey caches pre-parsed bytecode for scripts, which can
         | immediately start executing in the interpreter. (I believe
         | roughly the same is true for other engines. See [1], for
         | example.) The tricky part of caching compiled scripts is that
         | they generally refer to various runtime state. For example, a
         | compiled script might include a check to verify the shape /
         | hidden class of an object. If you want to cache that code, you
         | also have to cache the shape tree. Multiply that out by all the
         | runtime state that is referenced from compiled code, and it's a
         | hard problem. We've talked about simpler mechanisms like
         | caching function hotness / the number of iterations before our
         | profiling data stabilizes, to trigger compilation earlier, but
         | we haven't gotten around to implementing it yet.
         | 
         | [1]: https://v8.dev/blog/code-caching-for-devs
        
       | JohnHaugeland wrote:
       | It's always surprising reading articles like this.
       | 
       | The hard truth is that Javascript does not actually achieve great
       | performance. It's squarely in the back-middle of the pack. It's
       | just that it's vastly improved versus its old self, and the
       | methodology to get there has been wildly complex.
       | 
       | Of the languages tested in the alioth benchmark, more than half
       | outperform node.
       | 
       | This should not be surprising, given its lack of basic containers
       | and algorithms, and the inability to make the real thing. (No, an
       | array containing an item and another array isn't a linked list.
       | No, Okasaki containers aren't the real thing. Your complexity
       | guarantees undermine your confident statements.)
       | 
       | https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
        
         | Spivak wrote:
         | So yes but comparing to C++ is probably not the choice that
         | will be presented by most development teams. You're probably
         | looking more at Python, Ruby, Go, and Java as the main
         | alternatives and JS suddenly starts tending toward the front of
         | the pack.
        
       | oblak wrote:
       | Missing "How" from title
       | 
       | Pretty cool article. I've been dealing with JS for years without
       | ever thinking too hard about how it all works
        
       ___________________________________________________________________
       (page generated 2021-11-24 23:01 UTC)