There’s no JIT in this case, because of the AoT approach. It’s similar, since the goal is the same (generating native code), but it allows much more optimizations to take place.
However, the proposed idea wasn’t to compile entire modules like Cython does, because the generated .pyx are working exactly like .pyc (so, keeping the dynamic nature of CPython in every single aspect), with the only difference that it isn’t ceval that is executed, rather the native code.
Since I’ve joined Python and its community I’ve read several times a “mantra”: we’re adults! This in the context of justifying the dynamic nature of the language, which moves more on developers’ shoulders the responsibility of how to write correct/good code.
Here is the same case: it’s up to the developer on deciding if/what specific parts of HIS/HER code to be less dynamic/more static, giving the compiler hints on how to generate “better” code (better in regarding the developer scope).
This isn’t in principle incompatible with the language and the extensions which were developed. Unless the developer decides differently, of course. Which means that it’s up to him/her to leave the relevant parts as dynamic, to allow such extensions to properly figure out what to do. In fact, you don’t need to turn everything to “static”, and consequently it certainly doesn’t make sense to keep everything dynamic wasting opportunities to optimize the rest of the code.
In short: those are just new features that leave to the developers more opportunity to better design/implement their code.
We’re just at the beginning and the JIT already provided some good results. Not exceptional, but in the context of the big burden which CPython brings in terms of legacy/constraints, it’s certainly remarkable. I’m pretty confident that more can be gained.
Honestly speaking, as long as:
the JIT code can be easily removed;
there are people willing to maintain it (and some are even paid by their companies!);
I really don’t see the problem…
This is an interesting idea. It’s worth exploring how much this sort of thing would help performance in real-world use cases. If the effect really is significant, it could be worthwhile. The mypyc compiler provides prior art for some of these options.
Among the specific ones you suggest:
annotations_as_types: not sure how this would help runtime performance
static_builtins and frozen_globals: these make sense at the module level; they could be used to generate more efficient bytecode for that module.
i8 etc.: If these types are useful, we should just provide them in the standard library; not clear to me how these would work as “compiler flags”.
single_inheritance: single inheritance does make a lot of things simpler, but I’m having a harder time understanding how this could be a module-level flag. Code in one module could deal with objects defined elsewhere.
These are already available both from cython and librt. The latter is probably more close to what you would want as it’s a pure CPython extension. Same goes for builtin containers. The literature is there, one just needs to figure out how would that look like with this “static” approach and how this should be integrated into Python.
Or we could simply leverage typing.Protocol and approach in the same manner as rust does with traits (which, as a concept, is close to how mypyc interprets this). So instead we give up entirely on inheritance and embrace composition.
At any rate, if you really want to go towards a more “static” Python language, then there has to be a serious discussion of the current shortcomings of the typing specification and how it is applied.
And to be perfectly honest, maybe I’m misunderstanding but I think the discussion is steering towards the idea of enforcing a more “statically typed” Python rather than how to deal with a better JIT. Maybe another thread should be in order? Because I for one am getting confused on what exactly is the final goal it is requested.
reading back through my post it’s clear I didn’t express the point I was trying to make well. I agree with this. The JIT will be less complicated and more effective if we allow developers to restrict how dynamic their code is to enable optimizations that other JITs or AOT compilers use to outperform python.
However, it is a long road to get to that final goal from “deliriously happy with a slightly less dynamic Python”. A major hurdle I see is with preserving the functionality that has gotten python to where it is that is built on these dynamic features. Deciding a new module is not dynamic has limited utility if it relies on a library that exposes an interface that relies on dynamic features (ie sqlalchemys heavy reliance on descriptors). The semantics of what ‘this module isn’t dynamic but uses a dynamic module’ are likely to be quite complex and need to be worked out.
I have a fondness for descriptors and would like to see that functionality optimized by the JIT. I’ll admit I have only just begun started learning the details of how they work and the challenges they pose to the JIT, so I’m not sure how much I can add beyond “wouldn’t it be great if they were faster”.
The point I am trying to make is modules don’t exist in isolation and scoping dynamic features should to take dependencies into account so that ideally the gains from restricted dynamism isn’t limited it to modules with no dynamic dependencies or is so restrictive the benefits aren’t useful in practice.
It would help me solidify my thoughts on this if there is a list of issues, threads discussing, etc what challenges dynamic features create for the JIT to develop my understanding of which features would be helpful to restrict and how that interacts with module interoperability. I’ve read through the Mark Shannon’s faster-cpython repo README, plans, and quite a few of the issues, as well as some of Brandt Buchers presentations and docs, but those seem to to mostly be focused on how things do or will work, and but don’t recall seeing anything along the lines of which language features pose insurmountable challenges to the JIT. Any guidance on this would be much appreciated.
I worry this is veering off topic, if so, let me know and I can move this discussion to another thread.
Because you can have specialized versions depending on the type. For example,BINARY_INT_OP
instead ofBINARY_OP.
This allows to skip come controls and other, internal, optimizations might also be possible.
Yes, it’s certainly possible, but I didn’t want to pollute the built-ins name space, and only enable them as per demand.
In fact, those are just examples, because you can have multiple versions of the same sized integers:
wraps the result to make it fit to the size;
saturates the result;
generates an overflow exception;
promotes the result to the next, bigger size;
undefined behavior (the developers knows what’s doing).
On top of that, you can have integers which are guaranteed to be “large-enough” for the given size, or that operates as fast as it possible with the given size.
The low-level world is a bit more complicated, but I don’t know if Python likes to embrace all this stuff or just limit itself to a certain class of integers with a precisely defined behaviour.
There’s no change from this PoV, since the specialized code can work seamlessly together with the generic one.
Think about the methods defined in a class: you have the normal (instance) members, class members, and static members, depending on the member definition and eventually of the proper decorator used. But what happens after that you do x.foo() is internally handled by Python.
I prefer to keep them separate and decide by myself which one to use as per my convenience/vision on how I want to model the world (usually, to me is → inheritance and has → composition, but I might take different decisions strictly depending on the specific use-case).
Opening another thread is fine, but IMO the last discussions have major implication on the JIT rather than to the normal CPython interpreter (which might continue to treat those proposals as NOPs and remaining simple, leaving all those implementation burdens to the JIT): JIT has more potential to generate much more optimized binary code out of those “hints”.