Python is compiled to a VM (that is CPython). The semantics and implementation of the VM haven't prioritized performance as much as some other systems. Here we're seeing improvements in the implementation. But the semantics will be the hard part, as those semantics limit the performance. For instance "a + b" is (I believe) compiled into bytecodes that pretty much follow the expression. But the implementation of "add" has to take into account __add__() and the possible function calls and exceptions and return types that implies. If you compiled that all down to machine code (which people have done) you'd still have to run all that code to check types and the existence of methods and so on.
Dynamic languages like Javascript have solved this problem by essentially caching the resolution of a particular expression that is executed very often. I don't see why this cannot be done in Python.
The expressions are dynamic, so they have to be evaluated every time.
Python is excessively dynamic, so it can't (conventionally) be sped up as easily as many other languages unfortunately. Finally some folks are being paid and allowed to be working on it.
I don't buy this. There are many contexts in which a smart JIT compiler can detect that an expression cannot be modified. Especially since, due to the GIL, python code is mostly non-threaded. They just didn't spend enough time to do the hard work that people spent on Javascript.
> can detect that an expression cannot be modified
Can you give some examples? I use Python a lot and it's absurdly dynamic. I've sometimes wondered if there'd be some way to let me as the programmer say "dynamicness of this module is only 9 instead of the default 11" but I'm skeptical of a tool's ability to reliably deduce things. Here's just one example:
a = 0
for i in range(10):
a += 1
log('A is', a)
In most languages you could do static analysis to determine that 'a' is an int, and do all sorts of optimizations. In Python, you could safely deduce that 99.9999% of the time it's an int, but you couldn't guarantee it because technically the 'log' function could randomly be really subversive and reach into the calling scope and remap 'a' to point to some other int-like object that behaves differently.
Would "legitimate" code do that? Of course not, but it's technically legal Python and just one example of the crazy level of dynamic-ness, and a tool that automatically optimizes things would need to preserve the correctness of the program.
I think the way it is usually done is that the JIT assumes, for example after profiling, that an expression has a specific type, generates code according to that assumption and adds strategic guards to check that the assumptions are still valid, with a fall back path to the original unoptimized code. Thanks to the magic of branch prediction the guards have little runtime cost.
This is exactly what a lot of the current round of optimization work focuses on. Things like optimistic dispatch on hot loops that fall back to fully dynamic on a miss.