JavaScript is JIT’ed where CPython is not. Pypy has JIT and is faster, but I think is incompatible with C extensions.
I think Pythons threading model also adds complexity to optimizing where JavaScripts single thread is easier to optimize.
I would also say there’s generally less impetus to optimize CPython. At least until WASM, JavaScript was sort of stuck with the performance the interpreter had. Python had more off-ramps. You could use pypy for more pure Python stuff, or offload computationally heavy stuff to a C extension.
I think there are some language differences that make JavaScript easier to optimize, but I’m not super qualified to speak on that.
Nonetheless, Microsoft employed a whole "Faster CPython" team for 4 years - they targeted a 5x speedup but could only achieve ~1.5x. Why couldn't they make a significantly faster Python implementation, especially given that PyPy exists and proves it's possible?
Not an expert here, but my understanding is that Python is dynamic to the point that optimizing is hard. Like allowing one namespace to modify another; last I used it, the Stackdriver logging adapter for Python would overwrite the stdlib logging library. You import stackdriver, and it changes logging to send logs to stackdriver.
All package level names (functions and variables) are effectively global, mutable variables.
I suspect a dramatically faster Python would involve disabling some of the more unhinged mutability. Eg package functions and variables cannot be mutated, only wrapped into a new variable.
Because JS’s centrality to the web and V8’s speed’s centrality to Google’s push to avoid other platform owners controlling the web via platform-default browsers meant virtually unlimited resources were spent in optimizing V8 at a time when the JS language itself was basically static; Python has never had the same level of investment and has always spent some of its smaller resources on advancing the language rather than optimizing the implementation.
Also, because the JS legacy that needed to be supported through that is pure JS, whereas with CPython there is also a considerable ecosystem of code that deeply integrates with Python from the outside that must still be supported, and the interface used by that code limits the optimizations that can be applied. Faster Python interpreters exist that don’t support that external ecosystem, but they are less used because that ecosystem is a big part of Python’s value proposition.
First is the Google's manpower. Google somehow succeeds in writing fast software. Most Google products I use are fast in contrast to the rest of the ecosystem. It's possible that Google simply did a better job.
The second is CPython legacy. There are faster implementations of Python that completely implement the API (PyPy comes to mind), but there's a huge ecosystem of C extensions written with CPython bindings, which make it virtually impossible to break compatibility. It is possible that this legacy prevents many possible optimizations. On the other hand, V8 only needs to keep compatibility on code-level, which allows them to practically switch out the whole inside in incremental search for a faster version.
I might be wrong, so take what I said with a grain of salt.
V8 was a much higher priority - Google hired many of the world’s best VM engineers to develop it.
Anything goes regarding changing object shapes, it is one step further than Smalltalk in language plasticity.
Even "simple" stuff like field access in python may refer to multiple dynamically-mapped method resolution.
Also, the ffi-bindings of python, while offering a way to extend it with libraries written in c/c++/fortran/... , limit how freely the internals can be changed (see the bug-by-bug compatibility work done for example by pypy, just to name an example, with some constraint that limit some optimizations)
Very true, but IMO the existence of PyPy proves that this doesn't necessarily prevent a fast implementation. I think the reason for CPython's poor performance must be your other point:
> the ffi-bindings of python [...] limit how freely the internals can be changed
PyPy pays for this by having slower C interaction.
Most people that parrot repeat Python dynamism as root cause never used Smalltalk, Self or Common Lisp, or even PyPy for that matter.
Also Python has a de facto stable(ish) C ABI for extensions that is 1) heavily used by popular libraries, and 2) makes life more difficult for the JIT because the native code has all the same expressive power wrt Python objects, but JIT can't do code analysis to ensure that it doesn't use it.
Which can change on the fly anything that is currently executing in the image.
Also after breaking into the debugger, the world can be totally different after resuming execution at the trap location.
Then there are nice primitives like a becomes: b. where all occurrences of a get swapped with b.