It's very hard to estimate how much speedup a JIT will get you on a dynamic language like python and x5 speedup seems unrealistic.
There are other lower hanging fruits, like optimizing core data structures (e.g: the implementation of python dicts) .
It's very hard to estimate how much speedup a JIT will get you on a dynamic language like python and x5 speedup seems unrealistic.
There are other lower hanging fruits, like optimizing core data structures (e.g: the implementation of python dicts) .
However, this speedup comes at the cost of being less dynamic. I'm not sure how much more optimized core python objects could be without sacrificing some of the dynamism some programs rely on. Python dicts are already pretty optimized as is.
YouTube also encountered the same problem. Their solution sounds kinda like "never use pickle, because it's slow. Use custom serialization".
You can have a 2x or 10x for many use cases speedup without a JIT, as PHP7 proved. You just need to start with a slow, not very optimized, implementation, which CPython pretty much is.
As for 5x, Javascript has had much more than speed bump than that with its JITs (compared to the interpreted Javascript pre-JSCore, Tracemonkey and V8 circa 1997-2005) and it's just as dynamic as Python...
>There are other lower hanging fruits, like optimizing core data structures (e.g: the implementation of python dicts).
Funny that you should mention it, because the author of the proposal has already done significant work (available since Python 3.3. or so) optimizing the dicts...
The fundamental issue is that python is a pointer machine: everything requires a dynamic lookup in memory.
Eg.,
x = [1, 2, 3]
len(x)
Here `x` is an actual string in memory which is a key in a locals() dictionary which holds values. (cf. with C where it is just a memory address).Likewise the list is a list of pointers (not a sequential array). And its heterogenous, ie., the contents can be of any type.
Likewise `len` is a string into a dictionary of functions which has to be looked up.
etc.
The whole thing is many levels of indirection. Applying an operation to a value (eg., even x + y) requires jumping around the memory of the machine many times.
This is necessary, in general, to deliver on the dynamic lang. features python provides.
Julia solves some of these issues by using static type information to ditch this dynamic behaviour. My suspicion is that python can follow a similar path (eg., above, x should be compiled to a static homogenous array of ints).
In general the point stands, the reason for slowness is indirection & reification. (Not sure why i'm downvoted).
len() only causes one dictionary lookup and then it's cached.
> This is necessary, in general, to deliver on the dynamic lang. features python provides.
It's the most obvious way to implement these features of dynamic languages but not at all necessary.
https://www.infoworld.com/article/2074780/avoiding-hash-look...
https://github.com/markshannon/faster-cpython/blob/master/ti...
Still I'm a bit skeptical ... his reasons for why others failed and he will succeed is not that convincing.
This is why PyPy reimplements standard library in RPython — so you can JIT-optimize it. But it feels like Mark Shannon knows nothing about these efforts — which is kinda strange considering his position of core CPython developer.
>There are other lower hanging fruits, like optimizing core data structures (e.g: the implementation of python dicts)
Unfortunately, you cannot easily implement efficient data containers without rewriting existent python code. The latter one relies heavily on dictionary-based access to pretty much everything, and you cannot easily convert "string hash" access into "record offset" access, because you cannot know a priori what object has what structure and converting hash into offset is basically the same dictionary lookup. For example:
a = A() a.field = varname + 1
What can you optimize here? What "varname" is? What A's structure is? Is "A" a class or a function? Not only you are unable tell the semantic of the code just by looking at the code — you can't even tell the semantic after you've examined the "A" and "varname" on some previous iteration, because somebody might've declared/modified those on outer scope or directly modified "A" or "varname".
Last year in my spare time I've been working on an unpublished library for python multitasking with shared memory structures (probably will make some blog post in few weeks and link it here), and I also encountered the problem of inherently inefficient implementation of python basic types. However, I'm yet to find the solution without breaking compatibility with existing code. For example, if you look at ctypes, they have some very efficient containers, but using them in a regular python code is a pain, and the c-python interface eats most performance benefits of efficient containers.
So what's really needed for optimization of python is some kind of python subset, like RPython but probably more human-friendly, so efficient containers can really become efficient while automatic optimizer can select or create automatically those efficient containers. Just like V8 JS engine does, which stores objects in records with static structure. It happens to works in JS for most cases. Countrary, in Python it does not work for most cases, that's why we have so much struggle optimizing the Python.