[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)