We're on the edge of a major jump in Python performance.
With the work done by the FasterCpython team and Python 3.11 due out in around a month,
your existing Python code might see an increase of well over 25% in speed with no changes to your code.
One of the main reasons is its new specializing adaptive interpreter.
This episode is about that new feature and a great tool called Specialist,
which lets you visualize how Python is speeding up your code and where it can't unless you make minor changes.
Its creator, Brant Booker, is here to tell us all about it.
This is Talk Python to Me, episode 381, recorded September 15th, 2022.
Welcome to Talk Python to Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Twitter where I'm @mkennedy and keep up with the show
and listen to past episodes at talkpython.fm.
And follow the show on Twitter via at Talk Python.
We've started streaming most of our episodes live on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube
to get notified about upcoming shows and be part of that episode.
This episode is sponsored by Microsoft for Startups Founders Hub.
Check them out at talkpython.fm/founders hub to get early support for your startup.
And it's brought to you by compiler from Red Hat.
Listen to an episode of their podcast as they demystify the tech industry over at talkpython.fm/compiler.
Brant, welcome to Talk Python to Me.
Thank you for having me.
I'm excited to talk about some of this stuff.
I am absolutely excited about it as well.
I feel there's a huge renaissance coming, happening right now,
or has been happening for a little while now around Python performance.
It's exciting to see, especially in just the last couple of years,
that you definitely see these different focuses,
whether it's, you know, improving single-threaded Python performance,
multi-threaded Python performance, you know, Python in the browser.
There's a lot of really cool stuff happening, right?
Yeah.
Oh, if we could talk WebAssembly and PyScript and all that,
that is a very exciting thing.
There's probably some performance side around it,
maybe something we could touch on,
but that's not the main topic for today.
We're going to talk about just the core CPython
and how it works, which is going to be awesome.
Some work you've done with the team there at Microsoft
and your contributions there.
Before we get into all that stuff, though,
let's start with your story.
How did you get into programming in Python?
Yeah.
So I originally studied computer engineering,
so like hardware stuff at UC Irvine.
Around my, like, third or fourth year, so like 2017,
I encountered Python really for the first time
in like a project setting.
Basically, it was a senior design project.
We get to kind of make whatever we want.
So we made this cool system that basically it's point cameras
at a blackjack table and it's X card counters.
And if you want to do something like that in, you know,
four months or whatever, Python is kind of the way to go.
Yeah, the CV stuff there is really good, right?
Yeah.
So it was NumPy and OpenCV,
and that was kind of my first exposure to this stuff.
And so I learned that I like developing software
a lot more than developing hardware.
So I kind of never looked back and just went full in.
About a year later, so like 2018,
I opened my first PR to the CPython Red Belt,
and it was merged.
And you're up to that.
What was that PR?
Oh, there's this standard library module
called Module Finder.
Basically, it's one of those ones
that's kind of just a historical oddity,
and it's still in there.
Basically, you can run it over any Python script.
It detects all the imports,
and so you can use it to build an import graph.
And I forget exactly what I was using it for.
I think it was for work.
And I ran into some bugs that had been just kind of lingering there for years.
And so I, you know,
submitted patches for a bunch of them.
And so that was kind of my first experience contributing to open source in general.
That PR was actually open for a while.
It was open for like, I think it was like a month or two or something like that.
So in the meantime, I contributed other things to like my Pi type shed,
but that's still my first open source contribution.
It counts the day it was open.
Yeah, it counts as a beginning or the end of the PR, right?
Yeah, exactly.
Because those can be very different things sometimes.
They can be very different.
You know, we have now in CPython,
we have the, just in Python, I guess,
we have the developer in residence with Lucas Schlinger.
He's there to sort of facilitate making that a lot faster.
And I feel like people's experience there might be picking up speed and improving.
Yeah, no, I think that's a great thing.
And in general, just seeing this kind of shift towards full-time Python core development,
you know, being funded by these big companies is really exciting to see.
I think it improves the end contributor experience, the user experience,
and just gets things done, which is nice.
I don't have any numbers, but I imagine there are fewer of those kind of lingering,
you know, months old PRs than there were back when I first started.
Yeah.
Way, way long ago, four years ago.
So far back in the past.
So long, so long, yeah.
So yeah, so that was like 2018, 2019,
I became a member of the triage team,
which is basically a team for people who are kind of more involved in Python
than just your average drive-by contributor.
So while they're not full core developers,
they basically can do anything a core developer can,
except vote and actually, you know, press the merge button.
So that was really nice because it made doing things like reviewing PRs easier.
Tagging issues, closing issues, that sort of thing.
A year later, I became a core developer.
How did you get the experience for that?
Like, how do you, you know, it's one thing to say,
well, I've focused on this module and here's this fix.
And it's another to say, you know, give me anything from CPython and I'll assess it.
Well, it wasn't give me anything from CPython.
And it was, you know, made it part of my morning routine to, you know, wake up,
go to a coffee shop, order a coffee, and just for a half hour, look at newly opened issues.
And for newly opened PRs.
Yeah.
Well, I focus mostly on PR review for new contributors.
Basically, I had filters set up that said, you know, show me all the PRs open the last 48 hours from a new contributor.
I thought, okay, my first contribution experience wasn't that great.
It'd be great if these people who have never opened a PR to CPython before can get a response within 48 hours.
Whether that's telling them to sign the contributor license agreement or formatting fixes or pinging the relevant core dev or whatever.
So that was kind of how I dove into that.
All this terminology that you're using, PRs and stuff.
No, this is great.
What I was going to say is that this is new to Python, right?
It wasn't that long ago that Python was on Mercurial or before then Subversion.
It's, you know, it's coming over to GitHub is kind of a big deal.
And I feel like it's really opened the door for more people to be willing to contribute.
What do you think?
Oh, it absolutely lowered the barrier to entry for people like me.
Like using the old bugstoppython.org was tough at first, but I eventually kind of got used to it just in time for it to be replaced with GitHub issues, which I much prefer.
But I have a hard time seeing myself emailing patches around or, you know, I have a lot of respect for people where that was the development flow a number of years ago.
So I became a core developer in 2020.
And I guess it was about exactly a year ago, almost exactly a year ago, I joined the Python performance team here at Microsoft.
Were you at Microsoft before then?
No, I was not.
I worked for a company called Research Affiliates in Newport Beach.
And I think you actually had my old manager, Chris Ariza, on the show a couple years ago.
I have had Chris on the show.
That's fantastic.
Small world, huh?
Yep.
Small Python world.
Yeah, small Python world.
Just a couple million of us.
No, that's great.
So this brings us to our main topic.
And, you know, let's start at the top, I guess.
You know, there's the faster Python team, I guess.
I don't know.
How do you all refer to yourself?
Yeah, we refer to ourselves as the faster CPython team.
I think internally our distribution list is the CPython performance engineering team, which sounds a little gnarlier, but it's a lot wordier.
That's a cool title to have on a resume, isn't it?
Yeah, I think I'll use that one.
There you go.
There's been a ton of progress.
And some of this work was done in 3.10.
But, you know, there's this article here that I got pulled up on the screen.
It says, Python 3.11 performance benchmarks are looking fantastic.
That's got to make you feel good, huh?
I mean, they were looking fantastic back in, what, June?
Yeah.
They're a small bit more fantastic now.
So, yeah.
Yeah, exactly.
This article's from June.
So this is a, it's only better from here, you know, from there, right?
It's really exciting.
And like I said, it's, this is a performance jump that at least since I've been involved in Python, we haven't seen this in a point release.
You know, where we're seeing 25, 30%, sometimes more improvements for pure Python code rather than kind of the 5, 10, 15% range that might be more typical.
And again, I think that's kind of a product of this very conscious effort, whether it's my team or that a lot of people that we interact with.
So, for example, Pablo, release manager, strength council member at Bloomberg has been working a lot on this stuff with us.
Outside contributors, do they come to mind or Dennis Sweeney, Ken Jin.
There's definitely been a focus on this and it's paying off, which is really exciting, like I said.
Yeah.
And maybe a little bit more in parallel instead of a cooperative effort, but there's also the Cinder folks over at Meta Facebook.
That's absolutely a cooperative effort.
You know, even though Cinder isn't necessarily, we're not merging all of Cinder back into CPython, several of the changes are being upstreamed into CPython.
And in fact, just earlier this week on Tuesday, we had, I think, like a two-hour meeting where the Cinder team walked our team through how their JIT works.
Yeah.
So it's, even though, yeah, it can be seen as a parallel effort, I think it's very collaborative.
I think also, I, like you, am very much blown away at the step size of the performance improvements.
It's just super surprising to me that a 30-year-old language and a 30-year-old runtime can get that much better in that short of a time.
Yeah.
And I think, again, I feel like I'm going to keep coming back to this, but having, you know, full-time people dedicated to this, having teams of people dedicated to this, I think that's a big part of sort of unlocking this.
Because some of the things that are required for those big jumps are kind of larger architectural changes that, you know, a single volunteer who's, you know, doing this on their free time probably wouldn't have been able to do without coordinating with others and without throwing, you know, a significant amount of effort at it.
Yeah.
I mean, there are people out there, core devs, who have done amazing stuff.
Shout out to Victor Stinner.
I feel like a lot of the performance stuff that I've seen in the last couple of years, he is somehow associated with, you know, some major change.
But the changes that are being undertaken here, they're much more holistic and far-reaching.
And it really does take a team, I think, to make it reasonable, right?
What's cool about the 3.11 effort is it's a combination of kind of both sorts of changes.
So we have, you know, a bunch of kind of one-off, very targeted changes, probably five or six or ten of those.
And then we have, you know, one or two of these kind of larger changes that, you know, we can build upon in the future.
And they're never really done, which, you know, that's exciting because it means we get to keep making Python faster.
Yeah.
It is exciting.
I think another area to just highlight real quick before we get into too much detail is, correct me if I'm wrong, but none of this is particularly focused on multi-core parallelism, right?
No.
So one member of our team, Eric Snow, he's basically the sub-interpreter guy.
Exactly.
Yeah.
So he is focusing most of his time on, you know, a per-interpreter kill and, you know, all that sort of stuff.
But, I mean, all the numbers that you're looking at here and all of our benchmarks, it's all single-threaded, single process.
If you're running Python code, it will get faster without you changing your code, which is awesome.
Yeah, it's super awesome.
I want to highlight that because if Eric manages to unlock multi-core performance without much contention or trade-offs or, you know, if you've got an eight-core machine and you get 7x performance by using all the cores, like, that would be amazing.
But all of this work you're doing applies to everyone, even if they're trying to do that stuff, right?
So even if somehow we get this multi-core stuff, the computational multi-core stuff working super well, the work that you're doing and the team is doing is going to make it faster on every one of those cores, right?
So they're kind of multiplicative initiatives, right?
So if we could get a big improvement in the parallel stuff, it's only going to just multiply how much better this aspect of it's going to be for people who use that, right?
It's great to be kind of pursuing all these different avenues because they pay off in different timeframes, right?
Some of these are longer efforts.
And in Eric's case, the 7x program for very long effort.
I think he's done a great job sticking with it and seeing it through.
And some of these we're seeing in point releases.
And so they absolutely build on each other.
Like you said, you can get a 7x increase from sub-interpreter just to throw out a number.
But Python as a whole got 25% or 30% faster than you're seeing much more than a 7x increase.
Right.
Absolutely.
So very, very exciting times.
Two things before we get into the details of a particular interpreter and stuff.
Tell me a bit about this team.
You know, I interviewed Guido and Mark Shannon a while ago, about a year ago, I suppose, about this plan when they were kicking it off.
And we didn't have these numbers or anything, but we did talk about what we were doing.
And he talked about the team that he's working with there.
So he said, certainly it's just more than the two of us.
You know, tell us about the team.
Yeah.
So I think there are seven people.
So there's Guido and Mark, Eric and me, as we mentioned.
Another core developer, Urit.
One other engineer, El.
And a manager for the team who also does some engineering effort as well and is a member of the triage team, Mike Dronfum.
Yeah.
Nice.
Yeah.
He worked on Pyoxidizer.
Pyodide.
That's right.
Pyodide.
Yeah.
I don't know why.
It's a pie.
There's some kind of like a molecule on the end.
Exactly.
Pyoxidizer.
That's right.
That's the foundation for PyScript, actually, which is quite cool.
Py3 is cool too.
The other question.
Yeah.
The other thing that I want to ask you about is, so we have these numbers and visibility into Python 3.11 that's got a lot of conversations going.
And people are saying they're looking amazing and fantastic and other nice adjectives.
But this is in beta, maybe soon to BRC.
I'm not sure.
What is Python 3.11 status these days?
3.11, we just released our last release candidate, I think this week.
Right.
So this, if basically the idea is this, the final release candidate and the actual 3.11 release should be the exact same thing.
Unless we find any serious bugs that we're fixing before 3.11 won, the release candidate is going to be 3.11.
Awesome.
This is as close to stable as any of the releases.
Almost there, right?
Okay.
Yeah.
Cool.
So the reason I ask that is a lot of the work you've been doing recently is probably on 3.12, right?
Yes.
Yeah.
So beta freeze, which is basically when we go from alphas into betas, and there's no more basically new features allowed at that point.
That happens every May.
And so everything that we've been working on since May goes into 3.12.
Are you excited about the progress you've made?
Yeah.
Very excited.
It seems to be still coming along well.
Yes.
Yeah.
And it's nice.
We still have a lot of time before the next beta.
Let's talk really quickly about the faster-cpython thing put together by Mark Shannon.
Guido called it the Shannon plan.
And the idea is, how can we make Python five times faster?
If we could take Python and make it five times faster, that would be a really huge deal.
And again, none of that is multi-core.
If you could somehow unlock all the cores and you've got eight, that's 35 or 40 or something like that.
This is an ambitious plan.
It started out with a little bit of work with 3.10.
Is that when the adaptive specializing interpreter first appeared, or did it actually wait until 3.11 to show up?
No, I don't believe we.
We don't have it at 3.10.
Yeah, I didn't think so either.
Missing something.
Yeah.
So that's in 3.11.
The rest of it looks accurate, though.
Yeah.
So then basically that was stage one.
Stage two is 3.11, where there's a bunch of things, including kicking over the interpreter.
We're going to talk about the specializing interpreter.
A bunch of small changes here.
And then stage three for 3.12 is JIT.
Have you guys done any work on any of the JIT stuff?
Right now, it's not looking like 3.12 will ship with a JIT.
We think there are other changes that we can make that don't require a JIT that will still pay off.
We're probably planning on at least experimenting with what a JIT would look like.
Like I said, we already have gotten kind of a guide towards Cinder's JIT.
And so we're talking kind of high-level architecture and prototyping and that sort of thing.
But I would be surprised if 3.12 shipped with JIT.
But it's a long effort.
So starting now is a big part of that.
Research is being done, huh?
Yes.
Actively.
Yeah.
Cool.
All right.
Well, this was put together quite a while ago, back in 2020, as here's our plan.
And of course, plans are meant to evolve, right?
But still, looks like this plan is working out quite well because of the changes in performance that we saw already in 3.11 beta.
And pretty fantastic.
There are a bunch of changes here about things like zero overhead exception handling.
I believe there was an overhead for entering the try block.
Every time you went in or out of a try accept block.
So even if I did try pass, accept pass, there was overhead associated with that.
Right.
So basically, we would push a block that says, oh, if an exception happens, jump to this location.
Now what we do is we realize, oh, we actually have that information ahead of time when we're actually compiling the bytecode.
So since the common case is that an exception is raised, then we can store that data in a separate table and say, oh, if an exception is raised at this instruction, then jump here without actually having to do any of that management at runtime.
So it's a little more expensive.
I believe if an exception is raised.
But in the case of an exception, it's not raised.
I think it is basically as close to truly zero cost as possible.
This portion of talk, I think it's a little bit more.
This portion of talk, Python to me is brought to you by Microsoft for startups, founders hub.
Starting a business is hard.
By some estimates, over 90% of startups will go out of business in just their first year.
With that in mind, Microsoft for startups set out to understand what startups need to be successful and to create a digital platform to help them overcome those challenges.
Microsoft for startups founders hub was born.
Founders hub provides all founders at any stage with free resources to solve their startup challenges.
The platform provides technology benefits, access to expert guidance and skilled resources, mentorship and networking connections, and much more.
Unlike others in the industry, Microsoft for startups founders hub doesn't require startups to be investor backed or third party validated to participate.
Founders hub is truly open to all.
So what do you get if you join them?
You speed up your development with free access to GitHub and Microsoft cloud computing resources and the ability to unlock more credits over time.
To help your startup innovate, founders hub is partnering with innovative companies like open AI, a global leader in AI research and development to provide exclusive benefits and discounts.
Through Microsoft for startups founders hub, becoming a founder is no longer about who you know.
You'll have access to their mentorship network, giving you a pool of hundreds of mentors across a range of disciplines and areas like idea validation, fundraising, management and coaching sales and marketing, as well as specific technical stress points.
You'll be able to book a one-on-one meeting with the mentors, many of whom are former founders themselves.
Make your idea a reality today with a critical support you'll get from founders hub to join the program.
Just visit talkpython.fm/founders hub, all one word, no links in your show notes.
Thank you to Microsoft for supporting the show.
I think that's the way it should be.
You know, exceptions, as the word implies, is not the main thing that happens.
It's some unusual case that has happened, right?
So not always, but often means something has gone wrong.
So if something goes wrong, well, you kind of put performance to the side anyway, right?
Yeah.
Well, and, you know, a lot of the sort of optimizations that you want to see, especially in, for example, JIT code or whatever, exceptions are the sort of thing that mess that up.
Where, you know, if an exception is raised, okay, get out of the TUDIC code, go back to slow land where we know what's going on and have better control of context and everything.
But yeah, no, it's really exciting to see.
It's really cool design.
Yeah, it was Mark Shannon who did this.
Mark Shannon did a lot of this.
There's a bunch of improvements coming along, but what I want to really focus on here is the PEP 659, the specializing adaptive interpreter.
And in addition to being on the team, you've created a really cool project, which we're going to talk about as soon as we cover this one, about how you actually visualize this and maybe even change your code to make it faster.
Understanding how maybe opportunities might be missed for your code to be specialized or adapted.
No, PEP 659.
I mean, the concepts are not too difficult, but the implementation is really hairy.
So I think it definitely deserves to be gone over 70.
Because it's really cool when you get down to how it actually works and what it's doing.
Is this the biggest reason for performance boosts in 3.11?
I think it's the most important reason for performance boosts.
I mean, any performance boost kind of depends on what you're doing, right?
Like we did, for example, Pablo and Mark worked together on making Python to Python calls way faster and way more efficient.
So if you're doing lots of recursive stuff, that's going to be the game changer.
If you spend all your time writing loops that just do try, accept, try, accept.
Yeah.
That one's better.
Yeah.
If you're, yeah, got it.
Try accept in tight lubes or, you know, if you're, you do lots of regular expressions,
then our improvements in that area will probably matter more than this.
But this is cool because we can build upon it to kind of unlock additional performance improvements.
Sure.
We can kind of get to that once we have a better idea of how exactly.
When I look at PEPs, usually it'll say what its status is and what version of Python it applies to.
And I see this PEP, it doesn't say which version it applies to and its status is draft.
What's the story here?
I'm actually not sure what the story is behind the PEP itself.
I think it's a good informational document that explains, you know, the changes that we did get into Python 3.11.
But I don't think the PEP was ever formally accepted.
As you can see, it's just an informational PEP.
So it's kind of more the design of what exactly we're doing and how we plan to do it.
Right.
Because it's not really talking about the implementation so much as like, here's the roadmap and here's what we plan to do and stuff.
Right?
Yeah.
There's a lot of pros in there that says, here's how we might do it.
But, you know, no promises.
We could change this literally anytime.
And we have.
Yeah.
The design has changed fairly significantly since this PEP was first published.
But we've updated that so it can remain.
Yeah.
Cool.
Okay.
So the background is when we're running code, it's not compiled and it's not static types.
Right?
It's because it's Python.
It's dynamic.
And the types could change.
They could change because it just uses duck typing and we might just happen to pass different things over.
I mean, we do have type hints.
But as the word there is, it applies as a hint, not a requirement like C++ or C# or something.
You have to have the CPython runtime be as general as possible for many of its operations.
Right?
Yeah, absolutely.
And beyond just types, things like I could create a global variable at any time.
I could delete a global variable at any time.
I could add or remove arbitrary attributes.
All these sort of very Pythonic things about Python or un-Pythonic, depending on how you're looking at it.
These are all things that would never fly in pilot length.
Yeah, and they mean you can't be overly specific about how you work with operations.
So, for example, if you say, I want to work with call this function and pass it the value of X.
Well, where did X come from?
Is X a built-in?
Is it a global variable?
Is it at the module level?
Is it a parameter?
Is it a local variable?
All these things have to be discovered at runtime, right?
For the most part, yeah.
Yeah, for the most part.
Part of this adapting interpreter is it will run the code and it says, look, every single time they said load this variable called X, it came out of the built-ins, not out of a module.
And so we're going to replace that code, specialize it, or quicken it.
I've heard of quickening, so I'll have to work on the nomenclature a little bit there.
We can clear up the terms a bit, yeah.
Yeah, yeah, but you're going to take this code and you're going to replace it and don't say just load an attribute or load a value from somewhere and go look in all the places.
You're like, no, no, no, go look in the built-ins and just get it from there.
And that skips a lot of steps, right?
Yep, yeah.
Or maybe I'm doing math here and every single time it's been an integer.
So let's just do integer math and not arbitrary addition operator.
With the huge asterisk that if it does become a global variable or if it does start throwing floats at your addition operation, that we don't say false or even produce an incorrect result.
Right, because you could say, use x.
But before that, you might say, if some value is true, x equals this thing.
And it goes from being a built-in to a local variable or some weird thing like that, right?
And if it overly specialized and couldn't fall back, well, bad things happen, right?
Yeah, it would be surprising if you were getting len, the len function from the built-ins over and over and over.
And then you, for some reason, define len as a global.
Python, you know, the language specification says it's going to start loading the global now,
regardless of, you know, where it made it be able to pull.
And that's the same for, you know, like attribute accesses.
If you used to be getting an attribute off the class and then you shadow it on the instance,
you need to start getting it from the instance now.
You can't keep getting it from the class.
Is it correct?
Right.
And this is one of those problems that arises from this being a static dynamic language that can be changed as the code runs.
Because if this was compiled, wherever those things came from and what they were, they can't change.
Their type was set.
Their location was set.
And so then the compiler can say, well, it's better if we inline this.
Or it's better if we do this machine operation that works on integers better or some special thing like that.
Right.
It doesn't have to worry about it falling back.
And I feel like that ability to adapt and change and just be super dynamic is what's kind of held it back a lot.
Right.
Yep.
And I like that word they used, adapt, because that's kind of a big part of how the new interpreter works.
You can change your code and the interpreter adapts with it.
If X starts being an attribute on the instance rather than from the class, well, soon the interpreter will learn that sometime later after running your program.
And then, OK, start doing the fast path for instance access rather than class access.
Let's start there.
How does it know?
Right.
It doesn't compile.
So it has to figure this stuff out.
Right.
I run my code.
Why does it know that I can now treat these things X and Y's integers versus strings?
Yeah.
So stepping back a little bit, like how does this new interpreter work?
So the big change, kind of the headline is that the bytecode instructions can change themselves while they're running.
Something that used to be a generic ad operation can become something that is specialized, which is kind of the specialized instruction is the terminology we use for adding two integers or adding two floats.
And so this happens sort of in a couple of different phases.
After you've run your code for some amount of time, basically after we've determined it's worth optimizing because optimization is free.
So if something's only run once, you know, it's module level code or a class body.
There's no, no reason to optimize that at all for like leader runs.
Yeah.
We have heuristics for, okay, this, this code is warm.
And that's, you know, a term that you hear in JITs often because it's a higher order for specializing.
Basically after we've determined that a block of code is warm, we quicken it, which is that term that you used earlier.
And this basically means walking over the bytecode instructions and replacing many of them with what we call adaptive variant.
And, you know, you can, you can see an example in the pet, but you have to kind of walk through that example.
If you have an attribute load, once the code is quickened after we've determined it's warm, we walk over all the instructions.
All of the load attributes by code instructions become load adder adaptive.
And what those adaptive instructions do is when we hit them, when we're actually doing the attribute load, in addition to actually loading the attribute, they will kind of do some fact finding.
They'll gather some information and say, okay, I loaded the attribute, did it come from the class, did it come from the instance, did it come from the plot, did it come from a dict, did it come from a module?
You know, there's, there's a bunch of different possibilities.
Oh.
And so using that information, the adaptive instruction can turn itself into one of the specialized instructions.
So the example you have here on the screen can either be load adder instance value or module or slot.
And what the specialized instructions do is really cool.
Basically, they start with a couple of checks just to make sure if the assumptions are holding true.
So for a load adder instance value, we, you know, check and make sure that, for example, the class of the object is as we expect.
Then our attribute isn't shadowed by a descriptor or something weird like that, or that we're not now getting it off of a class object or whatever.
And then it has a very optimized form of getting the actual attribute.
There's a lot of expensive work that you can skip if you know that, you know, you have an attribute that is coming directly off the instance.
Or another one is load adder slot.
Slots are really interesting for Python performance.
And, you know, they kind of capture more than a lot of the stuff, the difference of the possible and the common.
And by that, I mean, it's possible that every time you access a field off of a class, that it was dynamically added and it came from somewhere else and it's totally new and it could be any type.
What's more likely, though, is it's, you know, the customer always has a name and the name is always a string.
Right.
And with slots, you can sort of say, I don't want this particular class to be dynamic.
And because of that, you can say, well, it doesn't need to have a dictionary that can evolve dynamically, which improves the access speed and the storage and all of that.
And here you all are pointing out that, well, we could actually have a special opcode that knows whenever I access X, X is a slot thing.
Skip all the other checks you might have to do before you get there.
Well, yeah.
And even accessing the slot is going to be faster.
So I'm not 100% brushed up on how load adder slot works, but the slots are implemented using descriptors.
So to get the slot from your class, you still, or from your instance, you still need to go to the class, look up the attribute in the class dict, find the descriptor, verify it's a descriptor, call the descriptor.
Into that list.
Yeah.
Exactly.
We can do it even faster than that.
So even if you do have slots, this happens really fast without even any dictionary lookups.
We say, has the class changed?
No.
Okay, cool.
We'll get to this later, but we remember what offset the slot was at last time and we just reach directly to the object.
Grab it.
There's no dictionary lookups.
We're not calling any descriptors, doing anything like that.
It's about as close to, you know, a compiled language as a dynamic language would be.
Yeah.
Just verify it.
Class has a change.
Okay, reach in.
I remember where it was.
You know, I tried to kind of align this up saying the possible and the common.
Most likely your code is not changing.
And when it's well written, it's probably using the same type.
You know, it's not like, well, sometimes it's a string and sometimes like that is the quote seven.
And sometimes it's the actual integer seven.
Like it should probably always be one.
You just don't express that in code unless you're using type hints, right?
And they're not enforced.
Yeah.
And getting back to the adaptive nature and making sure that we are correct.
You know, if we had something that was a slotted instance coming through a bunch of times and that suddenly you throw a module in or something with an instance dictionary or something else,
or maybe the attribute doesn't exist or those quick checks that I mentioned that happen before we actually do the fast implementation of loading a slot.
If any of those checks fail, then we basically fall back on the generic implementation.
We say, oh, our assumptions don't hold.
Go back.
And if that happens enough time, then we go back to the adaptive form and the whole cycle.
So if I'm throwing a bunch of integers into an add instruction and then later I stop and I start throwing a bunch of strings into an add instruction,
we'll do the generic version of add for a little bit while we're kind of switching over.
But then the interpreter will kind of get the hint and start, you know, specializing for string edition later.
And it's really cool to see that happen.
This portion of Talk Python to Me is brought to you by the Compiler Podcast from Red Hat.
Just like you, I'm a big fan of podcasts.
And I'm happy to share a new one from a highly respected and open source company.
Compiler, an original podcast from Red Hat.
With more and more of us working from home, it's important to keep our human connection with technology.
With Compiler, you'll do just that.
The Compiler Podcast unravels industry topics, trends, and things you've always wanted to know about tech through interviews with people who know it best.
These conversations include answering big questions like, what is technical debt?
What are hiring managers actually looking for?
And do you have to know how to code to get started in open source?
I was a guest on Red Hat's previous podcast, Command Line Heroes,
and Compiler follows along in that excellent and polished style we came to expect from that show.
I just listened to episode 12 of Compiler, How Should We Handle Failure?
I really valued their conversation about making space for developers to fail
so that they can learn and grow without fear of making mistakes or taking down the production website.
It's a conversation we can all relate to, I'm sure.
Listen to an episode of Compiler by visiting talkpython.fm/compiler.
The link is in your podcast player's show notes.
You can listen to Compiler on Apple Podcasts, Overcast, Spotify, Pocket Cast, or anywhere you listen to your podcasts.
And yes, of course, you could subscribe by just searching for it in your podcast player,
but do so by following talkpython.fm/compiler so that they know that you came from Talk Python to me.
My thanks to the Compiler Podcast for keeping this podcast going strong.
Right, so we shouldn't, we being Python developers that write code that just executes without thinking too very much about what that means,
we should not have to worry or maybe even know that this is happening, right?
If everything goes as it's supposed to, it should be completely transparent to us.
Yes.
The only way that you should know that anything is different about 3.11 is your CPU cycles.
The cloud hosting bill is the end of the month.
You should be able to upgrade and if behavior changes, that's a buck.
Tell us about that.
Going from this adaptive version, the adaptive instance sounds slightly more expensive than the,
just the old school load adder, for example,
because it has to keep track and it does a little bit of inspecting to see what's going on.
But then the new ones, once it gets adapted and quickened, it should be much faster.
Is there a chance that it gets into some like weird loop where just about the time the adaptive one has decided to specialize it,
it hits a case where it falls back and it like it ends up being slower rather than faster before?
Well, yeah, the worst case scenario would be, you know, you send the same type through n number of times
and then right when it's going to try to specialize you, set it through a different type.
Exactly.
Whatever, if n is the limit, like if it goes to n plus one times and then trips it back.
Yeah.
So that would be sort of the worst case scenario.
But we have kind of ways of trying to avoid that if at all possible.
Okay.
So, for example, if we fail one of those checks, we don't immediately turn into the adaptive form.
We will do it after, you know, that check has failed a certain number of times.
And as just a concrete example, that number of times that we have hard coded is a prime number.
So it's less likely that you'll fall into these sort of patterns where it's like, oh, I send three ins through that string.
Three ins.
It'd be hard to, you know, get that worst case behavior without being intentionally malicious.
By the way, we got to keep in mind, like these are extremely small steps in our code, right?
We might have multiple ones of these happening on a single line of what looks like, oh, there's one line of code.
Like, well, there's five or however many instructions, bytecode instructions happening.
And some of them may be specialized and some of them not, right?
Yeah, exactly.
And so the overall effect definitely smooths out where, sure, you may have a worst case behavior at one or two or three individual bytecode instructions.
But your typical function is going to have much more than that, even a smallish function is going to have, you know, 20 or 30 instructions doing it, you know, real.
Yeah.
If you care about its performance, it'll be doing a lot there.
Exactly.
And so some will specialize successfully, some won't.
But in general, your code will get 25-ish percent faster.
Is there a way you could know?
I mean, is there a way that you might be able to know if it specializes or not?
We'll get to that.
But it looks like if I go and use the disk module, D-I-S, not for disrespect, but for disassembly.
Disassembly, yeah.
So you can say, you know, import disk and then maybe from disk, import disk.
You can say disk and give it a function or something.
It'll write out the bytecode of what's happening.
Does all of this mean that if I do this in 3.11, I might see additional bytecodes than before?
You know, instead of load adder, would I maybe see the load adder instance value?
Will I actually be able to observe these specializations?
If you pass a keyword argument to your disk utilities, then yes, you will be able to see.
Okay.
But if I don't, for compatibility reasons like load adder adaptive and load adder instance,
those are just going to return as load adder?
Load adder.
Exactly.
Okay.
Yeah.
So the idea is anyone that's currently consuming the bytecode,
they shouldn't have to worry about specialization because the idea is they're totally transparent.
You know, so they should only see what the original bytecode was.
But if you want to get at it, then yeah, if you get any of the disk utilities,
you could pass it adaptive through and that will show you the adaptive.
And again, you'll only see them if it's actually gets quick at meaning if you run it,
you know, a dozen times or something.
Okay.
So if I wrote, say a function and so often what you're doing,
if you're playing with disk is like you write the function and then you immediately write,
print out the disk output.
Maybe you've never called it.
Right.
And so that might actually give you a different,
even if you said yes to the specialization output,
that still might not give you anything interesting.
It won't give you it.
Yeah.
It'll just give you as if you hadn't passed the keyboard argument.
Because again, this all happens at runtime.
So if the code isn't being run, nothing happens.
You know, we don't specialize code that is ever run.
What counts as warm, Mike?
How many times do I got to call it?
The official answer is that's an implementation detail of the interpreter subject to change at any time.
The actual answer is either eight calls or eight times through a loop in a function.
So if your function has a loop that executes more than eight times,
or if you call it more than eight times.
So just calling your function eight times should be enough to quicken it.
Right.
Well, maybe that number changes in the future, but just having a sense, like, is it 10 or is it 10,000?
You know what I mean?
Like, where's the scale up before I see something?
Yeah, if you want to make sure it's quickened, but you don't want to take up too much time,
I'd say just run it a couple less at a time.
As short man, when we're writing tests and stuff, we do like 100 or 1,000.
Because that also gives it time to actually adapt to the actual data that you're sending.
Because it's not enough to just quicken it, then you'll just have a bunch of adaptive instruction.
They actually have to see something.
Yeah, well, now we're paying attention.
Like, great.
You need to have something to pay attention to, right?
Yeah, and you'll see that too.
Because if you have any sort of logic flow inside of your function,
when you're looking at the bytecode, any paths that are hit will be specialized,
but any paths that aren't obviously won't because we don't specialize dead code.
So it has a bit of a code coverage aspect, right?
You can think about it like that.
If you look at it, you might see part of your code and it's just,
it's unmodified because whatever you're doing to it didn't hit this else case ever.
Yeah, well, and that's what's really exciting about this too,
is when you're, if you run your code a bunch of times and then you call this on it,
you see a lot of information that would potentially be useful if you were,
for example, JIT compiling that function.
You see at every addition site, you're adding floats or ints.
You see at every attribute load site, whether it's a slot or not,
you see where the dent code is, you see where the hot code.
All that stuff is sort of getting back to what I said,
how this sort of enables us to build upon in the future.
Not only can we add more specializations or specialize more op codes or,
you know, do that more efficiently, we can also use that information to kind of infer additional
properties about the code that are useful for, you know, other faster, lower representation.
Yeah, I can certainly see how that might be.
Might inform some kind of JIT engine in the future.
Yeah.
I think the PEP is interesting.
The PEP 659, people can check that out.
But like you said, it's informational.
So it's not really the implementation exactly.
Yeah.
So let's talk a bit about your project that you did.
In addition to just being on the team, the personal project that you did,
that basically takes all of these ideas we've been talking about and says, well, two things.
One, can I take code and look at that and get that answer?
Again, kind of back to my code coverage example, like coloring code lines to mean stuff.
And then what's interesting about this, and we'll talk to this example that you put up,
is there's actionable stuff you could do to make your code faster if you were in a super
tight loop section.
Like I feel like applying this visualization could help you allow Python to specialize more
rather than less.
Yeah.
I mean, in general, this tool is really useful for us as maintainers of the specialization
stuff because we get to see, you know, where we're failing to specialize.
Because ideally, you know, if we've done our job well, you should get past your Python
without changing your code at all.
First and foremost, this is a tool for, you know, us in our work so that we can see, okay,
what can we still work on here?
But that is sort of a cool knockout effect is that if you do run on your code, you also
know where it's not specializing.
And if you know enough about how specialization works, you may be able to fix that.
But I would say a word of caution against, you know, like getting two in the weeds and
trying to get every single bytecode to do what you want, because that's, there are much
better ways of making it faster, right?
There are places where you're like, these two functions, I know we got 20,000 lines
of Python, but these two, which are like 20 lines, they are so important.
And they happen so frequently.
Anything you can do to make them faster matters.
You know, people rewrite that kind of stuff in Rust or in Cython.
Before you go that far, maybe adding a dot zero on the end of a number is all it takes.
You know, something like that, right?
That's kind of what I'm getting at.
Not to go crazy or try to think you should mess with the whole program.
But there are these times where like five lines matter a lot.
Yeah.
Okay.
Well, tell us about your project specialist.
One really cool thing that our specializing adaptive interpreter does is we've worked really
hard to basically make it easy for us to get information about how well specializations
are working.
So you can actually compile Python.
It'll run a lot slower, but you can compile Python with this option with PyStats.
And that basically dumps all of the specialization stats.
Yeah.
And you actually pointed out that you can, in the faster CPython idea section, it like lists
out like a...
Yeah.
So there's tons of counters.
So you can see that when we run our benchmarks, you know, load adder instance value is run
what, 2 billion times, almost 3 billion times.
And it misses its assumptions, 1% of those.
And yeah.
And so there's tons of these counters that you can dump.
And that's really cool because we can run our performance benchmarks and see how those numbers
have changed.
And even cooler than that, it allows us kind of separately to, for example, there's been
at least one case where we've worked with a large company that has a big private internal
app and they can run it using Python 3.11 and we can get these stats without actually looking
at the source code, which is really cool.
And so we want to help you be faster and we're working on the runtime, but we don't want you
don't want to show us your code.
And so we're not going to look at it.
And so those stats are really useful for kind of knowing, okay, 90% of my attribute access
were able to be specialized.
What about the remaining 10%?
Where are they?
So, you know, an additional question, like why couldn't they be specialized?
And that's something that's a lot easier to tell when you're looking at the code.
And so this tool was basically my original intention for writing it is, you know, once we get beyond,
you know, seeing the stats for benchmark and we run something on it, that makes it easy to
tell at a glance where we're failing and how.
Right.
It's like saying you have 96% code coverage versus these two lines are the ones that are not
getting covered.
Exactly.
You get a lot more information from actually getting those line numbers than from the 97%.
And so basically the way it works, we already talked about how in the disk module, you can
see which instructions are adaptive or specialized.
And all that this tool does, it visualizes.
Literally the implementation of this tool is just a for loop over this, where we just kind
of categorize the different instructions and then map those to colors and all sorts of crazy
concepts.
Yeah.
And for people listening, you know, imagine some code and here you've got a for loop with
the tuple decomposition.
So you got for I comma T in enumerative text that are going to do some stuff.
And it has the I and the T turned green, but then some dictionary access turned yellow.
And it talks about, is it not at all specialized?
Is it, did it try but fail to get specialized?
And things like that, right?
Yep.
Yeah.
So green indicates successful specializations.
Red are those adaptive instructions that are slightly slower and represent missed specializations.
Yellow and orange are kind of a gradient.
As we talked about, you know, one line of Python code easily be 10 or 20 byte code instruction.
Right.
Yeah.
It's kind of a way of compressing that information.
It's really.
And one thing I want to point out about this too is, you know, you'll notice it's actually,
you see characters within a line.
And this is something that's really cool because this is piggybacking on a feature that's completely
unrelated to specialization.
Originally, when I was writing this, it highlighted each line.
So you could see this line was kind of green or this line was kind of yellow.
But then I remembered, maybe you're familiar, in Python 3.11, we have really, really formative
tracebacks where it will actually underline the part of the code that has a syntax error that
has an exception that was raised or something.
And so that's the PEP that's linked first in the description there.
It's, you know, called fine-grained error locate or fine-grained locations tracebacks.
And so what that means is that previously we just had line number information in the bytecode,
but now we have line number and end line number and start column and end column information,
which means that due to this completely unrelated feature, we can now also map it directly to which
characters in source file correspond to individual bytecode.
That's super cool.
Yeah.
So for example, here we've got a string and you're accessing it by element, by passing an index,
and you were able to highlight the square brackets as a separate classification on that array indexing
or that string indexing.
Exactly.
So it wouldn't have been as helpful to just see that that line was kind of yellow-orange-ish.
You know, we see that the fast variable store was really, really quick.
We see that, you know, the modulo operation and the indexing of string by int wasn't that fast.
We weren't able to specialize it, but, you know, we were able to specialize, you know, the lookup of the name TILEN.
We weren't able to specialize the function column.
So that sort of information, that granularity is really, really cool.
Yeah, it is super cool.
And I think a good way to go through this, you know, you've got some more background that you write about here,
but we've already talked a lot about specializing.
Yeah, this is just summarizing the pet piece.
Yeah, exactly.
People can check it out there if they want the TILEN, the too-long-didn't-read version.
But you've got this really nice example of, you know, what may be in the first few weeks of some kind of Python programming class.
Write a program that converts Fahrenheit to Celsius and Celsius to Fahrenheit,
and then just tests that, you know, round-tripping numbers gives you basically the same answer back within floating-point variations, right?
Yeah, I really like this example because it, you know, as we'll show, it's kind of presented through the lens of performance optimization,
but it also does a good job of showing you kind of how the interpreter works and how those little tweaks kind of cascade.
Mm-hmm, absolutely.
And it highlights certain things you can take advantage of that, you know, if you just slightly change the order,
it actually has a different runtime behavior, which we don't often think about in Python.
We're doing C++.
We would maybe debate, do you dereference that pointer first, or can you do it in the loop?
Yeah.
But not so much here.
So let's go through, I mean, I guess just to remind people, Fahrenheit to Celsius, you would say X equals F minus 32,
and then you multiply the result once you've shifted the zero by five divided by nine,
and the reverse is you multiply the Celsius by nine divided by five, and then you add the 32 to shift the zero again.
And basically that's all there is to it.
And then you go through and say, well, let's run the specialist on it to get its output.
And maybe talk about this first variation that we get here.
Yeah.
So as we were talking about, you know, the red indicates is adaptive instructions, and the green indicates the specialized instruction.
So we can see here that some things were specialized very well.
For instance, look at a cert roundtrip.
You know, that's great green.
Because it's in that hot loop, we got enough information about it to say, okay, a cert roundtrip is being loaded from the global namespace.
And it's a Python function.
So we can do that cool Python to Python call optimization.
And, you know, that's basically as fast as any function call in Python is going to be.
But some things aren't specialized.
So the things that jump out, you know, the things that we may want to actually take a closer look at would be the map, which is, you know, yellow and red.
So, for instance, we can, the loads of the local variable f in that first function and the load of the constant 32, those are yellow because the map that they're involved in, the subtraction operation, wasn't specialized.
But the individual loads of those instructions were specialized.
I see.
So half yes, half no for what they were involved in.
Yeah, yeah.
Green plus red equals yellow.
So, yeah.
So that subtraction wasn't specialized.
And the reason is, is in 3.11, just based on heuristics that we gathered, we determined it was worth it, at least for the time being, to optimize int plus int, float plus float, but not int plus float, float plus int.
And so what we're doing here is we're subtracting a float and an int.
And so we're able to see that that isn't specializable.
But if we were to somehow change that so that it was two floats, two ints, then it would be specializable.
Right.
Because when I look at it, it looks like it absolutely should have been specialized.
I have a float minus an int.
The int is even a constant.
Like you're, okay, well, this is standard math.
And it's always a float and it's always an int and it's always subtraction.
Right.
It seems like that should just, well, the math should be obvious and fast.
But because, as you pointed out, there's this peculiarity or maybe an implementation detail for the moment.
Absolutely an implementation detail.
Yeah, yeah, yeah.
Yeah.
If it's an int and a float, well, right now that problem is not solved.
Maybe it will be in the future, right?
It seems like pretty low hanging fruit, but you've got all the variations, right?
Yeah.
Specializations aren't free.
So for instance, like when we're running those adaptive instructions, we need to check for all of the different possible specializations.
So every time we add a new specialization that has some cost.
And basically we determined, at least for the time being, like I said, that we've tried to do int plus float and float plus int.
At least based on the benchmarks that we have and the code that we've seen, it just isn't worth it.
Sure.
Okay.
Yeah.
And it's something like int plus int is very easy to do quickly.
Float plus float is very easy to do quickly.
Int plus float, there's some coercion that needs to happen there.
Right.
So it's already constant anyway.
Yeah.
So it's not something that, it's something that caught some time to check, but we don't have a significantly faster wave.
It doesn't happen on a register on the CPU or something.
Yeah, exactly.
Okay.
So then you say, well, look what the problem is.
It's float in int where we have f minus 32, which seems again, completely straightforward.
What if they're both floats?
Well, what if like, you know, it's going to end up a float in the end anyway.
How about make it 32.0 instead of 32?
Yep.
And then bam, that whole line turns green, right?
Yep.
Yeah.
So now you're basically doing that entire line using fast local variables and look like native floating point operation.
You're just adding literally two C doubles together, which is.
Yeah.
And this is what I was talking about where you could look at that line and go, oh, well, I just wrote the integer 32, but I'm doing floating point math.
It's not like I'm doing integer math.
So if you just put, you know, write it as a constant with a zero, you know, dot zero on it, that's a pretty low effort change.
And here you go.
Python can help you more and go faster, right?
Yeah.
And that's not a transformation that we can do for you.
Because if F is an instance of some user class that defines dunder, so that would be a visible change if it started receiving a float as a right hand argument.
So those are things that we can't do while making the language still.
Right.
But your specialist tool can show you.
And again, figure out where your code is slow and then consider whether they're just like, well, we only got 100,000 lines.
Let's who's assigned to specializing today.
Yeah.
And it also requires, this is a simpler example, but it does require, you know, a somewhat deep knowledge of how the specialization work.
Because for things other than binary operation, it's not going to become clear what the fix is.
You just see kind of where the, I hesitate to even call it a problem, but you see where there's the potential for improvement, but not necessarily how to improve.
Yeah.
And then you have another one in here that's, I think, really interesting because it, so often when we're talking about math and at least commutative operations, it doesn't matter which order you do them in.
Like five times seven times three.
If it's the first two and then the result or multiply the last two.
And, you know, unless there's some weird floating point edge case that, you know, the IEEE representation goes haywire.
It doesn't matter.
Right.
And so, for example, here you've got, you know, to finish off the Fahrenheit to Celsius conversion, it's the X times five divided by nine.
And that one is busted too for the same reason, right?
Because it's a five.
Yeah.
Because, so this is kind of for two reasons.
This line isn't as good as it could be under ideal conditions.
So, you know, X is a floating point number, five is an integer.
So we have the same problem.
We specialize multiplication for int and int and float flows, but not for int and float.
And the division is a different problem.
We don't try to specialize division at all for the reason that it's kind of problematic because the right hand side could be zero.
And then you have to check for that and all sorts of things that you need to check for that make it not as much of a payoff.
So, you know, we have both an operation that we can't specialize, but it isn't being specialized.
And then another one that we're not even trying to specialize at all.
But then back to the commutative thing, you're like, well, what if we did the division, right?
What if we did the division, like, parenthesis five divided by nine?
And then X times that, right?
Yep.
And so wading through more of these implementation details, Python's compiler, you know, we have a bytecode compiler.
It's not a compiling to machine code, but it can perform simple optimization.
So by changing the order of operations, the bytecode compiler sees, oh, five divided by nine.
I can do that at compile time once rather than at runtime literally every time.
That's never going to change.
And so by changing that order of a problem.
Yeah.
Sorry.
Regardless of specialization, that's better anyway.
Right.
Because that happens when the PYC file is generated or when the equivalent thing in memory is generated.
And then it's just known as a constant, right?
Yeah.
And you're doing no division of runtime anymore.
So you turn this from two operations, one of which is pretty expensive, to just one operation.
All you're doing is a multiply by a constant.
Oh.
Yep.
And so you can see that, you know, once we apply that transformation to our code, everything's all break green.
This is as specialized as it is.
Right.
Because in Python 3, five divided by nine is a floating point, right?
Yes.
Doesn't module it out or whatever.
Yeah.
So then it becomes float times float, which then can be specialized.
And that first division part is something that is done at runtime when it first runs, but only once, which is fantastic.
Like parse time, basically.
Yeah.
So, yeah, this function or these functions, this code is much better as a result of just understanding.
Yeah.
And this transformation isn't something that Python can do for you because it changes the semantics of the language.
Again, if X is some user object, then it can serve the types that are being passed to it.
Yeah.
If it implements multiply, it expected to receive the five.
It didn't expect to receive 1.2715 or whatever the heck that is, right?
Yep.
Yeah.
Cool.
All right.
Well, this is a really cool tool.
I definitely encourage people if they're listening, just, you know, come over and just there's pictures of code and color.
Just scroll quickly through it to see what we're talking about.
And I find it super valuable because it highlights with color right on the code that you wrote.
It doesn't spit out the byte code and say, here's the byte code improvements, but it highlights your code and says the code you wrote is being improved by Python or not being improved by Python here.
Right.
And just understanding that it might not matter.
And it might matter a lot to you.
It depends.
Yeah.
And another thing to highlight, too, that's kind of different about this tool from maybe most tools that you would use is, you know, this isn't this isn't static analysis.
It's not like my pie or pilot where it's running over your code just in its file.
You actually need to run your code under this tool for it to be able to do it.
Because, again, all this happens at runtime.
So it's only after running the code that specialists walk over and see where you're running the code out.
Enough, right?
Yes.
Yeah.
So, for example, if I just had this function and I didn't actually call test convergence at the bottom there, the dunder name equals paint, everything would just be white because nothing actually ran.
Right.
So in this example here, you've got, let's see, two, four, six, eight, nine.
Surprising that number.
You have nine test values that you're passing in and you're looping over all those values and testing it.
You need to, if you're going to apply this to your code, it's super important that you come up with a scenario of representative data.
And for now, n greater than eight.
Who knows?
Or something that's loopy.
It's, you know, it runs loops eight times or something.
Basically, if the same bytecode instructions are being executed a bunch of times, that's how we tell that it's hot.
Whether that's in a loop or for repeated calls.
I can see it's pretty easy to forget that.
And people might run it and go, it didn't do anything.
It did nothing.
It's just all white.
Yeah.
Maybe you should add, I mean, I'm not, I'm not trying to issue a, an audio PR or anything, but maybe it should have some kind of warning.
Like if there's zero color at all, like a warning, like, are you sure you ran it?
Because we don't think you did anything.
I actually really liked that request.
Do that.
Yeah.
Cause you would know, right?
Like, you'd know if like I've colored nothing in any color whatsoever.
Yeah.
And I look at him like, oh, I did something wrong.
But someone else is going to be like, Fran did something wrong.
Yes, exactly.
Got to protect, got to protect my reputation.
Well, and just like limit the issues being raised.
Like how many times you want to say, did you remember to call, call it enough times?
Yeah.
Cool.
I definitely think people should check this out if they're interested in seeing how the adaptive specializing, specializing adaptive interpreter from Python 3.11 is applied to their code.
I guess also other caveat, like really not super handy.
If you try to do this with 3.10, you got to have 3.11.
It refuses to run under 3.10.
Yeah.
Cause I accidentally made that mistake enough times where I just had the Python environment I had acted with 3.10.
I'm like, not working.
Darn.
Yeah.
It's all like again.
It doesn't exist.
Yeah.
All that stuff.
Yeah.
So, so yeah, no, you need to be running at 3.12, but, or sorry, 3.11 or 4.3.12.
But, you know, as you showed earlier, you can download it from Python.org.
My favorite way of installing Python versions, PyN, has had 3.11 dev for a while now.
They also have 3.12 dev for crazy and you want to try it out.
But, but yeah, you do need, you do need a 3.11.
Fantastic.
Well, really great work.
I think it's quite a contribution.
It really highlights all the work that's being done in 3.11.
So well done.
All right.
Now, before you get out of here, I got to ask you the final two questions.
If you're going to write some Python code, if you're going to work on specialist, what
editor do you use?
I use VS Code.
Okay.
Right on.
And then notable PyPI package, something you came across, like, oh, this thing is cool.
Maybe not the most popular, but something that you want to give a shot at.
Yeah.
I thought about this.
Can I say two?
Is it only limited?
Two is fine.
Okay.
Cool.
So there's two.
I really like creative packages that kind of blur the line between like, what is Python
and what isn't.
Okay.
So the first one is called PyMetal 3, PyMTL 3.
This is so cool.
It allows you to design hardware using Python.
Interesting.
Okay.
And you can basically design everything from just a small, you know, set of logic gates
to a full chip, then export it to Verilog.
You run it on an FPGA.
And, you know, this is kind of my hardware background coming through.
Right, right, right.
Yeah.
My brother is actually studying at Cal Poly San Luis Obispo right now.
And he is on a research team that's designing an entire processor in Python.
And so basically the processor itself is designed in Python and you can test it with Python.
So they're testing with hypothesis.
And it's a really cool, creative way of kind of doing this sort of stuff.
Yeah.
You don't need any special hardware to make it happen either, right?
Yeah, exactly.
You can just run it on your local machine and now you've got a RISC-V chip running.
Nice.
Yeah.
So the other one is another kind of cool, weird, low-level hardware package.
I don't know if I count this pip installable.
It's called PeachPy.
I don't know how well maintained it is, but you have to do that thing where you tell pip
to install from like a GitHub link.
All right.
Well, you can pip install from a GitHub link.
Yeah, yeah, yeah.
You got to give it a really weird URL instead of just the name.
Yeah, yeah.
Okay.
Got it.
Sure.
And so this is super cool.
It's an x86-64 assembler in Python.
Okay.
So with this, you can basically implement a compiler or if you like it, a just-in-time
compiler for basically x86 hardware.
So what this allows you to do is in your Python code, it takes care of doing things like allocating
hardware registers and labeling jumps and all that sort of stuff, calling conventions.
And so you can specify exactly what assembly instructions you want to execute, assemble them,
and then it will package them up in a Python function object and you can call your assembly
code from Python, which I think is so cool.
Wow.
And it actually executes as assembly instructions?
Yes.
Yeah.
So it's like faults and everything.
Yeah.
Yeah, of course.
It's the most common thing it does.
Yeah.
But I mean, like a simple example, you can pass in a Pyaltic pointer and then add, you know,
eight or 16 or whatever to get the type of it and then return that and it will return.
Yeah.
Very cool.
So you can see the code example on here for PeachBuy.
I'll put the link in the show notes.
But you do things like create an argument and then you create a general purpose register
and you load the argument onto the register and you might call the ISA SSE4 operation or
whatever.
Pretty cool.
Yeah.
Two really good ones.
All right.
Final call to action.
People are interested in specialist and exploring the specializing adaptive interpreter.
What do you tell them?
I think the most important thing you can do is download or if you're feeling like it,
build Python 3.11 and try running it for yourself.
See if your code gets faster.
It probably will.
If it doesn't, then specialists could help show you where it's not.
And if that is surprising to you, then you could report it to us.
You know, if like, oh, my code got slower for some reason, you know, and it looks like
this specific pattern, what's causing it.
That's something that we care about.
Yeah, I suspect this interpreter is something that's never done.
It's clear you could always add more cases.
You could make it decide sooner or easier or more accurately when and how to specialize and
add more by.
There's like a lot of stuff you could do, right?
As opposed to, well, yeah, now you read CSV files.
That part is done.
Yeah.
And again, if you're feeling up to it and you've got a huge pure Python app, you can
even compile with stats and dump that out and take a peek at it.
I think you showed our repository earlier where we have our issue tracker, where we kind
of just spitball ideas and keep track of work in progress.
Yeah, this one.
If you go to the issue tracker here.
Sorry, I was wrong.
And the ideas one.
Yeah.
So you've got it's faster dash CPython slash ideas on GitHub.
Yeah.
So if you go to issues there, that's all our work in progress.
And if you have experience optimizing dynamic languages or if you see a cool research paper
something that you want us to know about opening issue here and where things get done.
Yeah.
Fantastic.
Well, thank you for making Python faster.
I think it's really, really important.
I mean, I'm always a little bit conflicted because I have some pretty complicated web apps
that get a decent amount of traffic and they've been fine, like really, really fine.
You know, handful of milliseconds response time.
I mean, they're doing all sorts of madness with databases and HTML and all kinds of stuff.
So on one hand, I don't know if I need Python to be faster, but on the other, people are
deciding which language they're going to choose and where they can do their work.
And sometimes either perceived or real reasons, people think Python is not fast enough.
Right.
And so this is important work that will really help some people and will help the community
to be stronger.
So thank you.
We love Python program.
Right on.
Cool.
Well, thank you so much for being here.
It's great to chat with you.
Thanks again for having me.
Yeah, you bet.
Bye.
This has been another episode of Talk Python to Me.
Thank you to our sponsors.
Be sure to check out what they're offering.
It really helps support the show.
Starting a business is hard.
Microsoft for Startups, Founders Hub provides all founders at any stage with free resources
and connections to solve startup challenges.
Apply for free today at talkpython.fm/foundershub.
Listen to an episode of Compiler, an original podcast from Red Hat.
Compiler unravels industry topics, trends, and things you've always wanted to know about
tech through interviews with the people who know it best.
Subscribe today by following talkpython.fm/compiler.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show.
Open your favorite podcast app and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play,
and the direct RSS feed at /rss on talkpython.fm.
We're live streaming most of our recordings these days.
If you want to be part of the show and have your comments featured on the air,
be sure to subscribe to our YouTube channel at talkpython.fm/youtube.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
I really appreciate it.