AOT instead of JIT

I think we view reactions differently. I don’t consider the opinions and arguments of other fellow members to be negative feedback :smiley:

My idea actually is very simple: if there is going to be an official JIT approach to Python, then I think we would be better off with an AOT approach, especially given that the current improvements are good enough, and the expected improvements with JIT are going to be in the future, not so much in the next releases.

“static” as in static type checking.

And there are sufficient duck-typing proponents that any eventual
shift toward strict typing in the interpreter is likely to end in a
fork so people who want “Typed Python” can go do their own thing
while people who prefer “Trad Python” don’t have to care about that
any longer.

1 Like

My idea actually is very simple: if there is going to be an
official JIT approach to Python, then I think we would be
better off with an AOT approach, especially given that the
current improvements are good enough, and the expected
improvements with JIT
are going to be in the future, not so much
in the next releases.

People who think a JIT compiler approach is useful are writing one.
Now you can write an AOT compiler (since you don’t seem to like the
existing ones) and then both of you can compare notes. Let us know
how it turns out?

This form of statement is sufficiently incorrect that it has a name: it’s a non sequitur. The inference “publishing an official AOT compiler for Python would be better” does not follow from the (true) premise “an integrated JIT design is being implemented for the reference Python interpreter”.

There are currently sufficient people involved in core development with a sufficient interest in developing first the specialising optimising interpreter, and subsequently the associated microinstruction JIT, that they have been able to successfully make the case for the Steering Council to approve the inclusion of that work in the reference interpreter (and the core devs not involved in the work have been collectively persuaded that it will be sufficiently beneficial and sufficiently non-disruptive that the SC’s decision is a reasonable one).

Those volunteer resources are not magically convertible to the resources needed to develop an official AOT compiler in the style of a project like Nuitka.

A lot of Python’s runtime flexibility comes from the notion of “code is data” with first class runtime representations of things like types, functions, bound methods, and more. This lets you do a lot of cool things (like build runtime types based on column headings in a data file), but it also comes at an intrinsic runtime cost, because the runtime can’t trust that the world won’t change out from under it without notice (such as objects changing their nominal type while retaining their runtime identity).

This forces AOT compilers for Python to make a choice: they either enforce the same kind of type stability requirements that regular AOT-compiled languages like Rust or Go do (meaning they implement a Python-like language rather than implementing Python itself), or else they generate more complex code that includes full support for the kind of flexibility that a dynamic Python interpreter offers (limiting their ability to offer genuine speedups relative to approaches that embed a full Python interpreter for runtime use).

Folks interested in AOT compilation generally choose the first option, since there are a lot of scenarios where compiling a Python subset is going to be an easier path to improving code efficiency than rewriting in a language that intrinsically improves efficiency by reducing the level of dynamism permitted at runtime (this isn’t even an interpreted-or-not distinction, as interpreted languages like Java and C# gain performance benefits for regular code by making introspection and data-driven dynamic behaviour in runtime code more expensive).

All of that combines to mean that AOT compilation of Python subsets remains an interesting area of investigation and experimentation, but also one that is entirely orthogonal to the idea of using JIT techniques to improve the efficiency of the reference Python interpreter. The problem scope is different, the techniques involved are different, the levels of interest in various individuals are different. They simply don’t have any overlap beyond the broad notion of “Hey, wouldn’t it be nice if Python programs could use less CPU time when executed?”.

13 Likes

There are currently sufficient people involved in core development with a sufficient interest in developing first the specialising optimising interpreter, and subsequently the associated microinstruction JIT, that they have been able to successfully make the case for the Steering Council to approve the inclusion of that work in the reference interpreter

Lets say welcome to JIT! although I’m already happy with Python 3.12 anyway!

A lot of Python’s runtime flexibility comes from the notion of “code is data”

I’m aware of Python’s history and its associated with Lisp (“code as data”) and Smalltalk (“dynamic classes”), and I think many people enjoy them and make use of them, I personally never made any use of them, and in most cases my classes are with “slots”, dataclasses always.

So I’m pretty sure there are cases were such flexibility is needed, but not in most cases, so I’m ready for tradeoffs and I can convince my entire company to accept tradeoffs also!

or else they generate more complex code that includes full support for the kind of flexibility that a dynamic Python interpreter offers (limiting their ability to offer genuine speedups relative to approaches that embed a full Python interpreter for runtime use).

I have to agree here, leaving some code as .pyc and calling CPython could be much more efficient than injecting complex code to dynamically resolve type and do proper allocation at runtime.

Folks interested in AOT compilation generally choose the first option

Exactly what I mean, a new AOT compiler for similar syntax but with totally different semantics, that compiles with expected and enforced type annotations and with lightweight runtime facilities (ARC/GC…etc). For legacy code, CPython can still be utilized.

I think CPython is very well-optimized and does a pretty good job already regarding what it is meant to do. However, I think improvements will reach their limits.

If that’s what you’re interested in, then you may want to check out Mojo: Why Mojo🔥 | Modular Docs

I won’t repeat their explanation of their goals here, but they sound very aligned with what you’re looking for.

Note: while the Mojo standard library is published at GitHub - modularml/mojo: The Mojo Programming Language, the compiler itself is still proprietary for now (as per this FAQ answer)

1 Like

Thanks for mentioning Mojo. I’ve been experimenting with it for about a year now, and it promises more than what I expected because it has the features I like in Rust as well! Rust and Python are almost all I use these days.

I think there is still a long way ahead, the type system is still missing critical things I care about (e.g. Union and generic bounds) and compatibility with Python will remain a challenge. There is a totally different logic under the hood (e.g. Ownership vs ARC as in Python), and keeping up with what Python is up to will remain a challenge.

Nevertheless, I think Mojo is an interesting project, and I wish that Python enters the same space as well.

Why, though, given that Mojo exists?

The Mojo mission statement and comparisons to other projects make it clear that they genuinely understand the problem space, the team members have relevant backgrounds in comparable language implementation design & development projects, their published road map is solid, and they have financial backing from Modular (and Modular have a vested interest in the project since their primary products all depend on it).

The Modular team’s reasons for designing Mojo as a Python superset rather than attempting to use Python as-is are valid, and they’re already using CPython to ensure full interoperability with the established Python ecosystem. In a very real sense, Python has already entered this space, via Mojo.

Anyone else looking to enter the same (or similar) space needs to address all the same questions that the Mojo dev team have, and make the case that their approach is a better one to consider. It’s fine if folks want to do that, but there’s zero reason the push for it needs to come from members of the existing CPython core development team (let alone those members already working on the integrated JIT compilation support).

1 Like

I am glad to hear that a CPython core developer welcomes Mojo as part of the Python family, it is a very inclusive and humble statement.

I presumed that the JIT is just experimental, and the PEP is still a draft, which might be rejected, but anyway I still think that the reference implementation of Python will be as AOT compiler at some point, meanwhile, the JIT is welcomed!

I think Mr. Lattner and his team are very capable people with deep knowledge of compiler technologies, but maintaining a superset is way more difficult than creating an AOT implementation while maintaining the old one. Why? In the latter case, both are under your control with feedback from the same community.

1 Like

I don’t think that follows. There is very little feedback that the SC receive that isn’t public, and even that is generally only private for a limited term (it’s usually commercial organisations providing courtesy advanced notice of future public announcements so the SC aren’t surprised when they happen, as well as discussions of sponsorship opportunities).

The reason Mojo isn’t alone in being a Python superset or variant in the acceleration space is that there is genuine performance value in being able to tell an AOT compiler to bypass regular Python semantics in some cases (think cdef in Cython or fn in Mojo).

This is also where the cleverness of Mojo choosing to embed CPython directly shows through: no matter how fast CPython moves, Mojo will be able to support Python code using the latest constructs, even if Mojo’s own syntax takes some time to catch up. It won’t be able to accelerate that code, but Mojo already explicitly distinguishes between the AOT compiled Mojo files and the interpreted Python files, so that limitation won’t surprise anyone.

3 Likes

Making a superset is rarely successful in the programming languages field. There are rare cases where this can be successful, not just for technical reasons, but largely due to management challenges. C++ as a superset of C is a success story because the two languages operate in a similar manner, and changes take ages, targeting a similar community to a large extent.

Mojo, at its core, is a ‘refined Rust’. It has a similar syntax to Python, but with radically different programming model compared to standard Python that revolves around concepts like ownership, borrowing, and lifetimes, along with low-level optimization constructs that regular Python lacks and that regular Python developers are unfamiliar with.

So, I don’t think an AOT compiler for Python as it is (with automatic reference counting) and Mojo are in the same category. There are other projects that might fall into this category, like Codon (OSS but with restrictive licensing). I believe there is zero chance for Mojo to become a superset of Python in the future. The similarity to Python’s syntax is a good strategy to bootstrap Mojo, but the two languages are radically different.

But that’s the point, isn’t it? AOT compilation requires a “radically different programming model” in order to statically fix types and objects ahead of time.

(I have never written a compiler or a sophisticated interpreter, so perhaps I misunderstand.)

2 Likes

So, on what basis do you think that an AOT compiler for Python is even possible? Is there any evidence that anyone has made one that, in your opinion, counts as an “actual” AOT compiler? As far as I know, all Python AOT compilers will fail this criterion.

No, it’s not necessary, Mojo presents a very interesting perspective, which I’m quite comfortable with since I also write a lot of Rust, but Python, as it stands, could have an AOT compiler with no changes to the syntax and minimal adjustments to the way code is written (e.g. reassignment of variables to a different type as declared or inferred would not be possible).

ARC generally offers nice trade-offs regarding runtime overhead compared to the conveniences it provides, and it is suitable for high-performance applications (Swift already has ARC, despite also being inspired by Rust).

Codon (came before Mojo) has already proven this with very nice integration with CPython.

I think the main issue is fragmentation, where things are not in sync, and the community becomes further divided. I believe this task should have a final solution coming from the Steering Committee and the Python community, rather than fragmented efforts by attempting alternative implementations every once in a while.

This sounds like a major breaking change to me

2 Likes

Codon doesn’t support arbitrarily large integers like cpython does, doesn’t support dynamic tuple length, and handles floats differently. It also completely changes the concurrency model.

I think you have a very rosy image of the kind of gains compilation will give you without completely breaking the language, and no data to back it up. Regardless, it’s a non-goal to break nearly every existing Python program ever written in ways that are extremely hard to spot.

5 Likes

Static typing involves certain trade-offs, If you’re not prepared for it, CPython can still be used for the dynamic features you want to retain.

I believe you’ll sacrifice minor conveniences for a significantly improved implementation of your program.

Those who implemented dynamic classes in Smalltalk have come to regret this decision because it not only impacts performance but also compromises the structural integrity and security of objects. I think Guido himself has mentioned this, and this was the reason behind adding slots to classes.

Reassignment to a different type is generally considered poor programming practice, unless you’re aiming for spaghetti code.

I mentioned Codon as an example for the main idea, not to discuss the implementation details of data structures in particular.

No, the language is not going to be broken, dynamic code can still be executed through CPython.

Regarding the data to back it up, I would like to elaborate here:
Most changes in Python in recent years are geared toward more static typing and performance optimizations. The ton of static type checkers and other tools that are published publicly, along with others used internally, and the various alternative implementations with millions spent on them, all of these are clean and clear data that this is what Python should take seriously, and this is what actually matters for Python’s future.

What else could be done to combine these efforts and produce a single modern solution that deals with performance problems and type safety other than an AOT compiler?

Warning, this is a rant.

I’m sorry but this thread has been a useful waste of valuable time. The idea itself is not based on any rock solid argument but rather an imaginary wish of sorts, no actual data to make a compelling argument, no idea of what level of impact such work would have on cpython, not aware of how many programs around the world could be affected and so much more.

I think there’s already enough python AOT compilers and attempts already. There doesn’t need to be an “official” AOT compiler unless there really really really has to. And that of course needs to be backed by compelling evidence that the idea will do more good than harm. That evidence can then be presented and debated upon. Once it’s satisfying then you can go ahead and write a PEP and you’ll have the backing of the core dev team that you’ve convinced. Other than that, just enjoy the language.

17 Likes

I suggest to lock this thread.

4 Likes