Hacker News new | ask | show | jobs
by montblanc 1705 days ago
Is the main difference in performance between JS and the others (Perl, Ruby, PHP) the fact that JS is JIT'ed and the others are not? I mean JS used to be slowish and until V8 came, what is V8 doing? Why can't Python / Ruby do the same thing?
5 comments

>I mean JS used to be slowish and until V8 came

There was JS Engine performance war before Chrome v8 came around. From JavaScriptCore to whole bunch of monkeys [1] from Mozilla. Google v8 just makes the competition super heated and every one of them were working around the clock trying to out compete each other on the latest benchmarks. ( That was the dark era when Browser only cares about JS benchmarks scores and nothing in real world )

It would not be an exaggeration to say all the man hours on JS JIT is more than Perl, Ruby and PHP's VM combined. That is why I often said Ruby is the only Top 10 language that gets little to no funding and backing of FAANG. So both YJIT and Sorbet are much needed contribution from Stipe and Shopify. Along with help from Github and Gitlab ( hopefully :) ).

The VMs that gets more resources than JS would be JVM and .Net. And JVM is a monster on its own. Easily a multi billion dollar of investment over all these years. Or something using less man hours and resources like LuaJIT. But then Mike Pall is a super human.

[1] https://en.wikipedia.org/wiki/SpiderMonkey

On-going project to clone Mike: https://github.com/LuaJIT/LuaJIT/issues/45
Python does, via pypy [1], which has very impressive performance and Python3 support. However, there are some subtle drawbacks, e.g., building from source takes hours instead of seconds (like CPython), and C extensions are slower. Still, the performance of pypy on a wide range of pure Python microbenchmarks is extremely impressive, and comparable to V8 (I've been collecting such benchmarks at [2] lately).

[1] https://www.pypy.org/

[2] https://github.com/sagemathinc/JSage/tree/main/packages/jpyt...

Not sure - Google pouring money into faster JavaScript execution is probably a big reason, but maybe JavaScript's comparative simplicity and fewer built-ins make it easier to get more out of it?
My understanding is that the main obstacle to higher performance Python is the huge value of preexisting extensions written in C. Maintaining compatibility with existing C extensions, or at least minimizing the porting effort for such extensions, puts a lot of constraints on the solution space.

I think one could radically change the way Python objects work internally, and have the C foreign function interface (FFI) wrap every object passed to a C extension in an API/ABI-preserving facade (which itself would wrap any objects returned from its methods). However, this would probably greatly slow down C extensions, which are often performance-critical sections of Python applications. It's also possible that there are portions of the C extension API that expose enough details of object internals to even make such facades herculean to implement. (I've only written some small simple C extensions and am not very familiar with the API.)

V8 didn't have to deal with API/ABI compatibility with any preexisting C extensions that may have made too many abstraction-violating assumptions about how objects and the VM worked.

Breaking too many important C extensions would almost certainly send Python the way of Perl 6.

Edit: as an aside, a big difficulty with JS is that objects can have their prototype changed arbitrarily at runtime. Even with Metaclass programming in Python, the class of an object can't be changed after creation, making it much easier to cache/memoize dynamic method dispatch. On the other hand, high performance implementation of Python's bound methods require a bit more flow analysis than you need in JS. In Python, if you write f = x.y, f is a "bound method" (a closure that ensure x is passed as "self" to y). It's expensive to create closures for each and every method invocation, so a high performance implementation would need to do a bit of static analysis to identify which method look-ups are used purely for invocation, and which look-ups need to create the closures because the method itself is passed around or stored in a variable.

> I think one could radically change the way Python objects work internally, and have the C foreign function interface (FFI) wrap every object passed to a C extension in an API/ABI-preserving facade

HPy is building an API abstraction layer which is designed to be used with both the CPython API and JITs. However, IIUC they are not proposing any changes to CPython itself, but rather to provide a smaller API surface and fewer JIT impedance mismatches when extensions are built against something other than CPython. The lead developer is a longtime PyPy developer.

https://hpyproject.org/ (https://github.com/hpyproject/hpy)

To a certain extent yes, but the largest obstacle is simply that, until rather recently, the core python team was hostile to including complex stuff like a JIT into the main python interpreter codebase.
What is simple in Javascript today? To me it seems feature bloated and you can do a thing in ten different ways.
Look past the syntax and it has very simple semantics. Ruby has extraordinarily complicated semantics due to an enormous core library.
I don't disagree but there is less to the base language than there is Ruby and Python (though all three languages continue to add new stuff). Even looking at the primitive types - JavaScript has three, Python has four, Ruby has... classes, but probably more base types.
MRI has more primitives of sort, but they're an implementation detail. Ruby itself conceptually only have objects.

MRI however implements some of them (integers, floats, symbols, true, false, nil, I might have forgotten one or two) using type tagged values instead of pointers to objects.

Type-tagging is easier to make fast for a highly gc'd dynamic language, as it reduces gc pressure substantially to not have to allocate lots of small objects without massive amounts of complex optimisations.

Actually Ruby 2.5 w/o MJIT is faster than Python 3.8 and Perl 5.28 at prime number crunching (using quite mundane stuff: basic math + for loops + hash/dict + array operations) but Node/V8, Dart and Luajit are still an order of magnitude faster along with the added JIT penalty. Racket, Julia and Lua are somewhere in between. The story used to be quite different two years ago, with Ruby being slower than both Perl and Python.
I'd be very surprised if well written Julia was in the middle speed pack here. I suspect there. are some known performance anti-patterns used if it's slower than Luajit.
It's most probably due to the startup and JIT penalty, but it was on par with Racket which is quite fast. I'm sure it can be optimized, but the implementation is quite similar to the other languages in order to have a level playing field. It's basically two nested for loops (in julia and racket is a single one with two indexes) adding valus to a set and another one doing set lookup and pushing into an array.
Ah, you're benchmarking wall time including startup and compilation?

If you're interested, I'd be happy to take look anyways and see if there are any easy, idiomatic performance changes that can made for the Julia code without changing the algorithm.

IMHO, basically money & talent. Google hires the best and pours money into V8. The very best developers can work full time and without any distractions. Also, Google is personally invested in making Chrome fast. Not to mention the vast sea of developers Google has and can throw at any project.

Other languages struggle in this regard. Comparatively I imagine far fewer developers working full time on Ruby/Python, not to mention they would have budget constraints to hire and retain talent.