The team at Instagram dropped the performance bomb on the Python world when they open-sourced
Cinder, their performance-oriented fork of CPython.
It contains a number of performance optimizations, including bytecode inline caching, eager evaluation
of coroutines, a method-at-a-time JIT, and an experimental bytecode compiler that uses
type annotations to emit type-specialized bytecode that performs better in the JIT.
While it's not a general-purpose runtime we can all pick up and use, it contains many
powerful features and optimizations that make their way back to mainline Python.
We welcome Dino Velen to the show to dive into Cinder.
This is Talk Python to Me, episode 347, recorded November 29th, 2021.
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 brought to you by Sentry and TopTal.
Please check out what they're offering during their segments.
It really helps support the show.
Dino, welcome to Talk Python to Me.
Hi, Michael.
Thanks for having me.
I'm really excited to talk to you.
You've been involved in a lot of projects that I've wanted to talk to you about over the years
and haven't yet.
So we're going to touch on a couple of those.
But we've got some really big news around Sender and some performance stuff that you all over at
Instagram are doing to try to make Python faster.
You did a really cool Python keynote, or not keynote, but talk on that.
So we're going to dive deep into this alternate reality runtime of Cpython called Sender that you
all have created.
That's going to be a lot of fun.
Yeah.
And it's a slightly alternate reality.
It's not that much of an alternate reality.
Just a little bit.
Before we do, though, let's just hear your story.
How'd you get into programming in Python?
I started programming when I was a teenager.
I got into computers initially really through BBSs.
Oh, yes.
Maybe.
Was this pre-internet?
This was pre- this is like dial up only.
You would dial into the BBS?
Oh, my gosh.
Yeah.
Like, you know, I had a modem.
Someone else had a modem sitting in their home waiting for people to call in.
You'd log in, send emails.
Post messages.
Take your turns on games.
Log out.
And someone else could log in and respond to your emails.
It was so amazing.
And send email meant wait for another BBS to dial in to connect to that one to like sync
its local batch of emails.
It was like peer-to-peer emails.
So weird.
Yeah.
I mean, or like there's a lot of local emails, right?
Where it's just on.
Right.
You're waiting for the other person to have a chance to log in.
But yeah, there's also that network, like a couple of different big networks.
It was such a different time.
I was not super into this as much.
My brother was really into it.
We had two phone lines so that we could do more of this.
Did you ever play Trade Wars or any of the games that were on there?
Trade Wars was awesome.
It was so good.
I think I would still enjoy Trade Wars.
It was so good.
I was still playing Trade Wars in college.
We like formed teams and like we're trying to take over some Trade Wars that was available
over the internet, actually, that you could like Telnet into and play.
And a lot of this BBS stuff had sort of found a home over Telnet for a while, hadn't it?
Yeah.
I think the main BBS software that I used there was used in St. Louis where I grew up,
which was World War IV with, I think it's still around and like available for you to
like, if you really want to host it on some internet server, but who's going to do that?
Incredible.
Okay.
So how's the BBS story fit into the programming side of things?
The BBS software that kind of was really popular, you could get a license to it for 50 bucks
and you got a source code for it along with it.
And there's a big active modding community.
And so, you know, I started off like taking people's mods and applying them and then trying
to make my own mods and like just ended up teaching myself C and initially very poorly
taught myself C, but then, you know, finally got good at this at some point.
How fun.
Yeah.
Did other people use your mods or were you running your own BBS or anything like that?
Where'd this surface?
I did a really bad job at running my own BBS.
I petitioned my parents for a second phone line, but I also wanted to use it for phone calls.
So to call my BBS, we had to like dial in.
And then like I had this device where you could punch four extra codes and it would connect you
to the modem.
So that was kind of annoying and didn't make it the world's most popular BBS.
And it was rather short web.
Heard some of the automation.
Yeah.
But I've published my mods.
My friends ran BBSs.
They pick up some of the mods.
I don't know that I was the most popular modder out there.
I should go and see if I could find them.
That might be terrifying though.
Yeah, that might be terrifying, but it could also be amazing.
Let's wrap up the BBS side of things with putting some bookends on the timeframe here.
What was the beginning baud rate and end baud rate of your BBS time?
4,400 to 57.6K.
Yeah.
So you took it all the way to the end there, but 2,400 probably meant it didn't require
putting the phone on to a device like the war games.
Never that bad.
Fantastic.
All right.
How about Python?
How'd you get to that?
I got into Python in a very weird way because I started working on a Python implementation.
Having really never touched or used Python before.
Obviously, I'd heard about it and it's kind of like significant white space.
That sounds weird.
And, but ended up really loving working on it on Iron Python, really loving the language
and the way it was designed.
It's a very, it gave me a very weird outlook on Python, I think, just because, you know,
I knew all sorts of weird corner cases about Python and the language and all the details
there.
But then didn't really know much about libraries and things like that.
And to some extent that continues today, but I get to write a lot more Python code today
too.
Sure.
But like always having been on the implementation side is a little strange.
It is strange.
And it is, I guess it would be a weird way to get to know the language.
So I feel like one of the real big powers of Python is that you can be really effective
with it with a super partial understanding.
Like you could have literally no idea how to create a function and you could still do useful
things with Python.
Whereas if you're going to jump in and create Iron Python, which we'll talk about in a second,
you have to start out, what are these meta classes and how do I best implement dynamic,
you know, dynamic objects and all this stuff.
That's like the opposite of starting with a partial understanding.
Well, how do imports work?
That was a big thing.
Yeah, yeah, yeah.
I remember when I learned about that, I'm like, wait, this is like running code.
It's not like an include file or a statically linked file or, you know, adding a reference
in .NET or something like that.
It's nope.
It just runs whatever's in the script.
And it happens to be most of the time it defines behaviors, but it doesn't have to.
Yeah.
And like, how do you pick what's going to get imported?
And yeah, the semantics there are so complicated.
Yeah.
There are some oddities of Python, but in general, it seems to be working well for people.
But I can see as implementing it, it could, you know, you could definitely be pulling some
hair out.
And I mean, so many things implemented are just, they're super safe.
Like they make a lot of sense.
There's just some weird corner cases that you run into that are, it's like, what's going on
here?
Yeah.
When I worked on Iron Python, we couldn't look at the source code that's like Python,
which made things really interesting.
Okay.
Because this predates .NET being open source and all that kind of stuff, right?
Yeah.
And so you don't want to be poisoned, poisoned by the ideas.
Yeah.
Okay.
How interesting.
iPhone was open source, but this was when Microsoft was still very much figuring out how they wanted
to approach open source.
And we're still very cagey about it.
It was very interesting.
Yeah.
They've come a long way and many companies have, I would say.
It's still, there's some idiosyncrasies, I guess there, but certainly it's a different
time now than it was then.
This was like what, 2008, 2009 timeframe-ish or 2005 maybe?
Yeah.
2005, 2006.
I think it was around Iron Python 1 out.
2006 sounds about right.
Yeah.
So that's a while ago.
Yes.
It doesn't sound that long ago to me, but honestly, it's a while ago.
Yeah.
It's like remembering the 90s is not 10 years ago.
It's true.
It's definitely true.
All right.
How about day to day?
What are you doing now?
Your Instagram, right?
Basically, I work on our fork of CPython, which we call Sender.
And my job is to make, my entire team's job is to make Instagram run more efficiently.
I mean, obviously, Instagram is a very large website that has a lot of traffic and it's a
very large Django app.
So we just spend our time trying to improve CPython and, you know, very specifically trying
to improve CPython for Instagram's workload.
We're very driven by kind of that as our sole direction.
And so it lets us make some interesting decisions and drive some interesting decisions.
But it's just really spending the day looking at what we can do to improve performance and
going off and implementing that and making Instagram a little bit faster.
So when we talk about Python and Django running Instagram, I put up a little post here of
something I did yesterday just to have some Instagram stuff to show.
Is that talking about the website?
Is that the APIs behind the scenes?
Like when you say Django runs Instagram, what are we talking about here?
So it's the website.
It's the APIs.
There's obviously some parts that aren't Django, but kind of everything that people's devices
are interacting with is going through the Django front end.
And there's also a bunch of like, you know, if we have asynchronous processes that need to
kick off and run in the background, that's kind of all handled by a Django tier as well.
So it's a good chunk of what's going on.
Yeah.
Nice.
This is probably one of the, if not the largest Django deployment there is, right?
This is a lot of, a lot of servers we're talking about, right?
I would assume so.
I don't know.
There might be something else pretty big out there.
Yeah.
I feel like the talk at the 2017 PyCon, remember that when we used to go to places where there
are other people and we would go and like be in the same room and stuff.
That was so nice.
I know.
It was so weird.
And there was a cool Instagram talk about, I believe that one was about disabling the
GC or something like that.
And I feel like they said in that talk, at least at that time, that was one of the largest,
not the largest Django deployment.
Yeah.
And we no longer disable the GC.
We fixed the memory leak.
So that's good.
Okay.
We're going to talk a lot about memory.
And honestly, this whole conversation is going to be a bit of a test, an assessment of my
CPython internals.
But I think that's okay because a lot of people out there don't know super in-depth
details about CPython.
And I can play the person who asks the questions for them.
Awesome.
I can try to answer questions.
Sure.
Well, we'll keep it focused on the part that you've been doing.
But during your talk, you mentioned a couple of things.
First, you said, okay, well, when we're running over on Django, we're running on, you say UWSGI.
I feel like this is a micro.
It used to be like a-
Micro Whiskey.
Yeah.
Micro Whiskey.
I don't know.
UWSGI, Micro Whiskey, whatever it is.
Yeah.
I feel like all these projects that have interesting names should have a press here to hear how
it should be pronounced.
It should be WSGI or Whiskey.
How do it?
Anyway, this micro Whiskey you guys are running on and understanding how it creates child processes
and forks out the work is really important for understanding some of the improvements that
you've made and some of the areas you've focused on.
So maybe we could start a little bit by talking about just the infrastructure and how actually
the execution of Python code happens over at Instagram.
So in addition to UWSGI, it's running on Linux, which is probably not surprising to you.
Literally zero people are surprised now.
Yeah.
I thought it was a Windows server.
Come on.
Or Solaris.
Yep.
Or a Raspberry Pi cluster.
Come on.
That'd be awesome.
So like one of the common things that people take advantage on Linux is fork and exec, where
you start up a master process and then you fork off some trial processes and they can share
all the memory of that master process.
So it's a relatively cheap operation to go off and spawn those trial processes and you get
a lot of sharing between those two processes, which reduces kind of the memory that you need
to use and all that good stuff.
And so the way UWSGI is working is that, you know, we are spawning our master process, going
off importing kind of all of the website.
Like we try to make sure that everything gets loaded initially and then spawn off a whole bunch
of worker processes, which are going to actually be serving the traffic.
And if something happens to one of those worker processes, then the master will come in and
spawn a new worker to replace it.
Yeah.
That kind of goes on and on and on.
And it's also not just about durability.
It's also about scalability, right?
If one of the worker processes is busy working on a request, well, there might be nine others
and the supervisor process can look and say, OK, well, I got some requests got to be processed
here.
This one's not busy and sort of scale it out.
And that also helps a lot with Pythons, GIL and stuff.
You can just throw more of these worker processes at it to get more scalability.
And at some point that kind of hits the database limits anyway.
So it doesn't really matter that much, right?
Yeah.
And I think like you would ski can auto tune.
I don't know exactly all the details of our settings.
Yeah.
There's a lot of advanced settings in there.
Yeah.
Like it can, you know, tune for memory for stall workers.
It's pretty smart.
Yeah.
But like, yeah.
There's actually a really interesting, I don't know.
Have you, maybe you've seen this.
There's a really interesting post called configuring UISG for production deployment over on
Bloomberg tech talking about all these knobs that they turn to make it work better and
do these different things.
And it's super interesting if these, like these tuning knobs are unfamiliar to Python people.
Yeah.
But the important takeaway here is when we're talking about running your code on a single
server, we're talking about five, 10, 20 copies of the same process running the same
code with the same interpreter.
Yeah, exactly.
You guys pay for bigger cloud instances.
No, I mean, you have your own data centers, right?
So you probably get bigger VMs.
This portion of Talk Python to me is brought to you by Sentry.
How would you like to remove a little stress from your life?
Do you worry that users may be encountering errors, slowdowns, or crashes with your app right
now?
Would you even know it until they sent you that support email?
How much better would it be to have the error or performance details immediately sent to you,
including the call stack and values of local variables and the active user recorded in the
report?
With Sentry, this is not only possible, it's simple.
In fact, we use Sentry on all the Talk Python web properties.
We've actually fixed a bug triggered by a user and had the upgrade ready to roll out as we got
the support email.
That was a great email to write back.
Hey, we already saw your error and have already rolled out the fix.
Imagine their surprise.
Surprise and delight your users.
Create your Sentry account at talkpython.fm/sentry.
And if you sign up with the code talkpython, all one word, it's good for two free months
of Sentry's business plan, which will give you up to 20 times as many monthly events as
well as other features.
Create better software, delight your users, and support the podcast.
Visit talkpython.fm/sentry and use the coupon code talkpython.
And so that impacts a lot of the decisions that we make.
We can talk about those more later.
I think another interesting thing about RUWSD and our deployments in general is that we're
also redeploying every 10 minutes when developers are...
Yeah, I saw that and that blows my mind.
So tell me about this rapid redeployment.
It blows my mind too.
And when I started it at Facebook, I guess now Meta, but it was Facebook back then, you
go through a process called Bootcamp where you spend your first several weeks just learning
about Facebook.
And one of the first things you learn is like, Facebook.com redeploys every three to four
hours.
I'm like, that's insanely fast.
And then land on Instagram.
We were redeploy every 10 minutes.
It's like, what?
Yeah, that's incredible.
Can you talk about why that is?
Is there just that many improvements in code changes going on?
Or is there some other like balancing reason that this happens?
Like a DevOps-y thing?
I don't know what all the original reasoning is.
It has a very nice...
So one of the nice things about deploying a lot is when something goes wrong, it's not
hard to figure out what caused things to go wrong.
So you're not looking at...
Right.
There's a bunch of small changes and each one gets deployed.
So you're not going back to the last six months or whatever, right?
Yeah, exactly.
Or I mean, even...
I mean, each of those deployments has a good number of changes in it.
And even if it was like four hours, it would be a huge number of changes that you have
to track things down through.
And it's also like, it's really satisfying from a developer standpoint in that, you know,
you land your change and it's rolling out in half an hour.
So I don't think anyone...
I don't know all the original reasoning, but I don't think anyone would really want to change
it just because it actually has some significant benefits.
It makes things interesting and challenging in some ways too.
But otherwise, I think it's really nice.
Yeah.
I...
It just never ceases to frustrate me or blow my mind how there's these companies just have
extended downtime.
Like, I'm not talking...
We pushed out a new version and in order to switch things in and out of the load balancer,
there's five seconds of downtime.
Or we got to run a database migration and it creates a new index and that's going to take,
you know, one or two minutes.
I'm talking...
We're going to be down for six hours on Sunday.
So please schedule your work around.
I'm just like, what is wrong with these companies?
He's like, how is it possible that it takes so long to deploy these things?
And if they had put in some mechanism to ship small amounts of code with automation, then
they would just not be in this situation, right?
Like, they would just...
They would get pushed somewhere and then something would happen and then they would have a new
version of the site, right?
It always baffles me when I end up at a website and it's like, we're currently down for service.
It's like, what?
Yes.
There's a website.
You're not supposed to do that.
The most insane thing...
I'll get off this thing, but it drives me crazy.
The most insane thing is I've seen websites that were closed on Sunday.
I'm like, what do you mean it's closed on Sunday?
Yeah.
Just go and turn it off when you go home?
Like, you know, it's open Monday to Friday sort of thing.
This is like, it was like a government website and I don't know why it had to be closed,
but apparently it had to be closed.
Yeah.
We have engineers standing by Monday through Friday to process your requests by hand.
Exactly.
We got to push the button.
No one's there to push the button.
Okay.
So I guess one more setting the stage stories here or things to know is that you run these servers quite close to their limits in terms of like CPU usage and stuff like that.
And then also you said one of the areas that you focus on is requests per second as your important metric.
Do you want to talk about those for a moment?
Sure.
So I don't know what the overall numbers under normal load are.
You know, I don't think the CPU load is necessarily super high.
But what we want to know at the end of the day is like how many requests can we serve under peak load?
And so what we can actually do is take traffic and route it to a set of servers and drive that traffic up to where the server is under peak load.
And we see how many requests per second a server is able to serve at that point, which gives us a pretty good idea of kind of what the overall level of efficiency is.
So when we make a change, we can basically run an A-B test where we take one set of servers that don't have the change, drive them up to peak load and compare it against another set of servers that have the change and drive those set of servers up to peak load.
And then compare between the two and see how many requests per second we end up getting and what the change is.
And we can do that to a decent amount of accuracy.
I think kind of like when we kick off a manual test, we try to strive for within 0.25%.
When we're doing releases of sender, I think we try to push it a little bit further by doing more runs.
So we get down to like 0.1% or something like that.
So we have a pretty good idea of what the performance impact of what those changes are going to end up looking like.
I think that makes a ton of sense.
You could do profiling and obviously you do.
And we do that too.
Yeah.
But in the end of the day, there's a bunch of different things, right?
If I profile against some process and say, well, this went this much faster in terms of CPU, maybe it took more memory.
And at production scale, it turns into swap, which means it's dramatically, you know, there's a bunch of pushes and pulls in there.
And this pragmatic, just like, let's just see what it can take now is interesting.
You all are in this advantaged situation where you have more traffic than any given server can handle, I would imagine.
Yes.
So you can tune in.
We actually run on one server.
Exactly.
We have a backup server.
It hasn't been rebooted in seven years.
You have the ability to say, well, let's just tune some of our traffic over to this one particular server to sort of see this limit.
Whereas a lot of companies and products don't, right?
Like I use this thing called Locust.io, which is just a fantastic Python framework for doing load testing to actually know the upper bound of what my servers can handle.
Because we get a lot of traffic, but we don't get 30,000 requests a second, lots of traffic, right?
And so I think this is really neat that you can actually test in production sort of beyond integration tests, not test that it works right, but send real traffic and actually see how it responds.
Because really, that's the most important thing, right?
Does it do more or does it do less than before?
And, you know, you brought up profiling and we still have to use profiling sometimes to like, you know, 0.25%, 0.1%.
That's still a lot of noise.
And like, so if there's some little micro optimization, we can still be like, okay, well, what's this function using after the change, you know, kind of across some percentage of the entire fleet, which is kind of amazing because the profiling is just running on production traffic sampled.
So for smaller things that ends up becoming super important.
Right. And you're making a ton of changes as we're about to dive into, but they're additive or multiplicative or something like that, right?
So if you make this thing 1% faster, that 5% faster, this 3% faster, all of a sudden you could end up at 20 to 30% faster in production, right?
Yeah.
And how do you do that math?
Well, we just add up the percents.
Exactly.
Where's Cinder? Here we go.
All right.
So when I saw this come out, when did you all make this public?
Shortly before PyCon?
Yeah, that's right.
Yeah.
Which would put it like February, March, something like that.
Yeah, something like that.
That sounds exactly right with this eight months ago.
So this is under the Facebook incubator.
You guys got to rename this.
It should be meta.
Yeah.
I wonder whose job that is.
I mean, permalinks.
Come on.
So it doesn't matter all that much.
It's Instagram, I guess.
But let me just read the first opening bit here.
I think there's a lot to take away just from the first sentence.
Cinder is Instagram's internal performance-oriented production version of CPython 3.8.
So performance-oriented.
We've been talking about performance.
We're going to get into a lot of the cool things you've done.
Production version.
So you guys are running on this on Cinder?
Mm-hmm.
Fantastic.
And we redeploy like once a week?
You redeploy the Cinder or CPython runtime, right?
Yeah.
Yeah, yeah, yeah.
So like the source that's up here is, yeah, if you go back and look at maybe a week ago
is what we're probably running in introduction at any given time.
Right.
Okay.
Fantastic.
And then CPython 3.8, because you've made a lot of changes to this that can't really move
forward.
So you picked the one, I'm guessing that was the most current when you first started,
most current and stable, and just started working on that, right?
So we do upgrade.
Okay.
It was previously built on CPython 3.7.
Oh, cool.
Okay.
There's hundreds or, I don't know if we're yet up into thousands of changes yet.
But there's a lot of dips that we've applied.
It's been, I mean, we've been working on it for, I've been working on it for three years
now and it predates me.
So we've upgraded 3.7.
We're going to upgrade to 3.10 next, which we're actually starting early next year.
So it's just a big and bold process.
And you've also contributed some stuff from Cinder to 3.10.
So that'll be, that'll be interesting as well.
That probably actually makes it harder to merge rather than easier.
We hope that makes it easier.
That is one of the things.
I guess you could drop that whole section, right?
You could just say, you know what?
We don't even need this whole enhancement because that's just part of Python now, right?
Okay.
Yeah.
That is the incentive for us.
One of the incentives for us to contribute.
Yeah.
Itamar out in the live stream in the audience says, we're close to 2,000 commits.
Oh my gosh.
Yeah.
That's awesome.
Itamar's going to be, he is now our kind of full-time dedicated resource to help us upstream
things.
Oh, fantastic.
That's, oh, to upstream it.
Itamar's job is to take the work you're doing here and then work on getting that into CPython
properly.
Okay.
We could have been doing such a better job.
I think we've upstream some little things, some slightly more significant things, but it's
something that we really need to be working on more.
Oh, that's fantastic.
We've got someone who's dedicated to it and obviously he's not just doing it in a vacuum.
We're going to help him.
But having someone drive that and make sure it actually happens is super important.
Yeah.
That's really cool.
I suspect that he and Lucas Lingo will become friends.
Yeah.
Lucas will be on the receiving end of that a lot.
I bet.
giving it the developer and residents over at CPython.
Cool.
All right.
So I guess let's talk about this.
Is it supported?
So right now the story is you guys have put this out here as sort of a proof of concept.
And by the way, we're using it, but not we expect other teams and companies to take this
and then like just run on it as well.
Right.
This is probably more to like work on the upstreaming side.
Is that the story?
Yeah.
And like let people know what we're doing.
If someone wants to pick it up and try it, that's great.
It's just mainly we're focused on our workload and making it faster and can't commit to helping
people out and making it work for them.
Right.
But as you just said, you are working on bringing these changes up to CPython and you already
have to some degree.
So that's, you know, that's pretty good.
I guess it also lets you all take a more specialized, focused view and say, you know what?
We want to make micro WSGI when it works off child processes.
We want to make it that happen better and use less memory.
And we're going to focus on that.
And if it makes sense to move that to main Python, good.
If not, then we're just going to keep those changes there.
Right.
Yeah.
And then, I mean, that's happened, I think with like, we've done some work around
immortalization of the GC heap, which is kind of a big improvement over not collecting.
We were talking about earlier.
And that didn't make sense for upstream CPython.
And so it's okay.
That's something that we just have to maintain.
Cool.
I was so excited when I saw this come out.
I'm like, wow, this is the biggest performance story I've seen around CPython for quite a
while.
And now there have been some other things as well.
We'll touch on at the end on how they come together.
But maybe walk us through what is Cinder?
What is this work?
And we can dive into some of the areas, maybe.
Sure.
You have immortal instances highlighted.
So we could start talking about that.
You want to start with JIT first?
I think.
Yeah.
Okay, sure.
If you think JIT makes sense.
Yeah, let's talk there.
So the JIT isn't what I live on day to day.
We have several other team members who are working on that full time.
But it's obviously a huge part of the performance story.
So the JIT right now is it's a method at a time JIT.
So it compiles each individual method.
It's, again, very tuned for our workload.
Kind of you can see here, like some of the descriptions of how to use this thing.
And it's mentioning this JIT list file.
So when we're using this in production, what happens is we compile all the functions ahead
of time inside of the master process before we fork off all this worker processes.
Because we want all that memory to be shared between the different processes.
So that's kind of a unusual mode for a JIT to work in.
Right, right.
They don't normally think about children processes and forking.
They just do their own thing, right?
Yeah.
It's just like, okay, I have this method.
It's gone hot.
It's time to jib it.
So it's used in this weird way.
At some point, we need to, I think, add support for kind of normal jitting methods when they
get hot.
Like we were at the point where we're talking about using Cinder a little bit beyond Instagram
within meta.
And so at that point, people are going to need something that isn't so heavily tuned to
UWSC.
The JIT does, it's, you know, entirely, we kind of boom in the full stack.
So it uses, I think, is it ASM shit?
Yeah.
It uses a library to do the X64 code generation.
Other than that, we go from a high level representation.
How close is the high level representation to just Python's bytecode?
There is a pretty good set of overlap.
There are also a lot of opcodes which kind of turn into multiple smaller things.
So like off the top of my head, I think like making a function involves setting several
different attributes on it at the end.
So there's something that says, make me this function, which is just a single opcode in CPython.
And there's several different opcodes which are setting those fields on it.
So it's pretty close, but maybe slightly lower level.
There's also a lot of opcodes in there for just kind of super low level operations.
So one of the things, the thing that I spend most of my time working on is static Python.
And so we added a bunch of things that support primitive math and simple loads and stores of
fields and lower level things like that.
So it's a mix.
Yeah, the static Python that we're going to talk about is super cool.
And is that possible because the JIT, like you can do whatever you want and then the
JIT will see that and then adapt correctly?
The JIT's really important to it because like it takes things that are usually tons of instructions
and turns them into a single instruction or a couple of instructions.
It's not 100% required.
Like we support it in the interpreter loop and kind of our goal is to do no harm and generally
like at least get the normal performance.
But the JIT being able to resolve things statically and turn them into simple loads is super important.
So from HIR, like we turn that into an SSA form and run a bunch of optimizations over it.
I think one really interesting optimization is rough count removal.
So we can see kind of these objects are either borrowed or just like that we'd have extra rough counts happening on them that we don't need to actually insert.
And we can just elide all of those, which is super awesome.
This portion of Talk Python and me is brought to you by TopTal.
Are you looking to hire a developer to work on your latest project?
Do you need some help with rounding out that app you just can't seem to get finished?
Maybe you're even looking to do a little consulting work of your own.
You should give TopTal a try.
You may know that we have mobile apps for our courses over at Talk Python on iOS and Android.
I actually used TopTal to hire a solid developer at a fair rate to help create those mobile apps.
It was a great experience and I can totally recommend working with them.
I met with a specialist who helped figure out my goals and technical skills that were required for the project.
Then they did all the work to find just the right person.
I had short interviews with two folks.
I hired the second one and we released our apps just two months later.
If you'd like to do something similar, please visit talkpython.fm/TopTal and click that hire top talent button.
It really helps support the show.
There's a lot of interesting stuff happening around memory that you all are doing.
Yes.
But one of them is this ref count and you make assumptions that are reasonable.
Like when I'm in a method call of a class, I don't need to increment and then decrement the self object.
Because guess what?
The thing must be alive because it's doing stuff, right?
And then it sounds like also maybe with constants, like the number one doesn't need its ref count changed and stuff like that.
You notice that and go, you know what?
We're just going to skip that.
One of the things we've done is the immortalization of objects.
And so we can also like the number one is going to be an immortal instance.
And so in that case, we can be like, okay, yeah, we don't need to deal with ref counts on this.
Unless, of course, like that number one ends up going off to somewhere that, you know, maybe doesn't understand the ref counting semantics of the JIT.
In which case, maybe we do have to end up inserting them.
Right.
Or like if it's going through like an if else or something where one of the branches, we have to end up ref counting.
So it's smart.
And it's important because with immortal instances, our ref counts are a little bit more expensive than normal ref counts because we have to check to see if the object is immortal too.
Right.
So they're just doing an increment on a number.
Yeah.
Okay.
So this, this immortal instances, this comes back to that memory thing that comes back to the turning off the GC, which you stopped turning it off.
It sounds like immortal instances are a more nuanced way to solve that same problem.
This is really about that fork and exact model.
Yeah.
So when we fork off those worker processes, they're initially sharing all the memory with the master process unless they happen to go off and write to it.
And ref counts are a really big source of writing to that shared memory.
And so what this does is takes all the objects that are present inside of the master process and runs through, marks them all as immortal.
And then from then on out, the trial process will be like, oh, this thing's immortal.
I'm not going to change the ref count.
Okay.
So this happens, you basically just scan the whole heap right before you do the fork and you're like everything, we're just going to clone this and it becomes unchangeable.
And then we'll just, at least with regard to its ref count and we'll go from there.
Yeah.
And then as long as ideally we also don't, we shouldn't have a lot of global mutable state.
I think people should be like, you know, if you think about what's in the master process, a process that's like classes and functions.
And people shouldn't be really going off and mutating those things inside of the worker processes.
Modules, yeah.
It seems like something strange is happening if that's going on.
Maybe let me ask you really quick or let you talk about really quickly this.
The real benefit here is on Linux, when you fork off these processes, if the memory itself hasn't been changed, that can be shared across the 40 or 60 processes.
But as soon as that memory changed, it has like a local copy has to be dedicated to that one worker process.
So silly stuff, simple stuff like I want to pass this string around that happens to be global.
And then it says, well, it's passed.
So you've got to add ref to it, which means now you get 60 copies of it all of a sudden.
Those really simple things, you are able to get lots better memory sharing, which then leads to cache hits versus cache miss and misses.
And there's like all these knock on effects, right?
Yeah.
And it's not just the string itself, right?
It's the entire page that the string lives on.
So, you know, you might have a 15 byte string with, you know, a 16 byte object header and you end up copying 4K of memory.
Because you changed a 6 reference number to a 7 or to a 5.
Yeah.
Fascinating.
Okay.
Do you think that Python, CPython itself could adopt this?
Would it make sense?
We tried to upstream it and there was resistance to it.
I mean, it is touching something that's very core and it's going to be a bit of a maintenance burden.
There are other reasons, I think, that people are now talking about wanting to have immortal instances.
So Eric Snow has been working on sub-interpreters for a long time.
And I think he has been interested in them recently for sharing objects between interpreters.
And I think Sam Gross's work on NoGill might have some form of immortal instances as well.
So maybe the core immortal instances support could land upstream at some point.
But, you know, maybe the code that actually is walking the heat and is freezing every day, maybe that's very Instagram specific.
And it doesn't have much value upstream.
It seems to me that there's probably a set of things that would be good immortal instances for almost any Python process that starts up, right?
Like before your code runs, everything there probably would be a good candidate for that.
And, you know, there's potential, like it's kind of scary because ref counts are so frequent.
And so adding extra code in the ref count process seems risky.
But if you can't freeze enough stuff that was kind of there before the program started up, that's super core and happening a lot, then maybe it does actually end up making sense for other workloads too.
Yeah, perhaps.
Okay.
So these immortal instances are one of the things that you all have done that's pretty fascinating.
And also a huge win.
Something like 5%.
Yeah, yeah, that's right.
It says right here.
Big win in production, 5%.
And does that mean 5% request per second?
Is that when you say 5%, is that the metric you're talking about here?
Yeah.
Have you thought about or tested, I'm sure you've thought about, like if this lets you run more worker processes off of increment that, that spawn worker process number?
I think the developer who worked on this was doing, did look at that number and was looking at tweaking the number of worker processes.
If I recalling, it got a little bit of pushback from people who were nervous about increasing it.
Don't mess with this number.
We never mess with this number.
What are you doing?
Yeah, but I hear you.
I'm just thinking, you know, if it really does create more shared memory, maybe it creates more space on the same hardware for you to actually create more.
And then that would just possibly allow even a bigger gain in request per second because there's more parallelism.
I mean, given that it was such a big win, it could have just been that we were already under significant memory pressure and it got us out of significant memory pressure.
Maybe we had the right number.
Maybe we had too many hosts.
I don't know.
Yeah, yeah, perhaps.
Perhaps.
But still, 5% is, as one of the changes, is still a pretty big deal.
Mm-hmm.
All right.
The next one on deck is strict modules.
Let's talk about strict modules.
I mean, we've talked about a little bit of things that are kind of related to this.
You know what I'm saying?
Like, if you have things that are going off and mutating your things in the master process, it's like, what?
That's kind of crazy.
So, strict modules work about performance, actually.
There's a little bit of performance thought behind it, but now they're really, we're not considering them as a performance feature at all.
They're more about a reliability feature.
And so you brought up early on how, like, Python modules, just going off, executing some code.
Who knows what that code's going to do?
Right.
So strict modules is an attempt to tame that process.
And what we do is we run static analysis over the code.
I mean, we are basically interpreting the code in a safe interpreter.
And if the module has any external side effects or depends upon any external side effects, we don't allow it to be imported.
And so we know that all the modules are side effect free that are strict.
When you say they're side effect free, does that mean that the importing of them is side effect free or all of the functions are also side effect free?
The importing of them.
Their functions can do whatever they want.
Got it.
They can call functions from other modules.
They can call functions from themselves.
If they call those modules at the top level while doing the import, then those functions need to be side effect free.
So where does this lead you?
What do you get out of this?
We get additional reliability.
So like, you know, Instagram, as I think maybe we mentioned this being a big Bono monolithic application.
Maybe we didn't get to that.
Yeah, I don't think we talked about that.
But this is not a hundred microservices type of thing, is it?
No, it's one giant application.
The thing that gets redeployed every 10 minutes is that giant application.
That makes the redeployment even more impressive, by the way.
Right?
Yeah, I mean, maybe it's nice in that it's one giant application because you just have to redeploy one thing.
Yeah, exactly.
It's not a hundred different things you got to keep in sync all at the same time, right?
Yeah.
Our PEs make that happen.
And it just happens behind the scenes as far as I'm concerned.
So, you know, if you have like, you know, if you import one module and it depends on side effects from another module and then something changes,
there's the import order, whether that's like state that things are depending upon, suddenly things blow up in production and your site doesn't work and everyone's really sad.
So this is like, we want to get to a world where our modules are completely safe.
We've used this.
We've experimented doing other things with this.
So like adding a hot reload capability.
We know the modules are completely side effect free.
Why not just patch the module in place and like let developers move on without restarting the website.
It has the potential to kind of really change the way we store modules.
So that we haven't gone down this route yet where instead of storing modules is a bunch of Python code that needs to run off and execute.
Could we store modules as like, here's a class definition.
Here's a function.
And can we lazily load portions of the modules out of there?
But we also have a really other different take on lazy loading that's in center now too.
Okay.
Yeah, that's pretty interesting.
Because normally you can't re-import something because maybe you've set some kind of static value on a class.
You've set some module level variable and that'll get wiped away, right?
I mean, you can call reload on a module.
But whether or not that's the safe thing to do, who knows?
Exactly.
All right, cool.
So I think one of the more interesting areas, probably the two that really stood out to me, are the JIT and StaticPython with the immortal objects being right behind it.
But StaticPython, this is your area, right?
What is this?
Yeah.
So this is an attempt to leverage the types that we already have throughout our entire code base.
So Instagram is 100% typed, although there are still some many types flowing around.
But you can't add code that isn't typed.
So we know the types of things.
Right.
You're talking traditional just colon int, colon str, optional str, that type of typing.
Yeah.
So why not add a compilation step when we're compiling things to PYCs instead of just ignoring the types?
Why don't we pay attention to the types?
Yeah.
So we have a compiler that's written in Python.
There's actually this old compiler package that started in Python 2.
There's this external, there's this developer on GitHub, PF Falcon, who upgraded it to Python 3 at some point.
And we upgraded it to Python 3.8 and made it match CPython identical for bytecode generation.
So we have this great Python code base to work in, to write a compiler in.
And we analyze the type annotations.
And then we have runtime support and a set of new opcodes that can much more efficiently dispatch to things.
There's a great, my coworker Carl Meyer had this awesome slide of calling a function during a Python talk.
And it was just like pages, well, it was one page and a very, very tiny font of the assembly of what it takes for CPython to invoke a function.
And then we're able to just directly call a function using the x64 calling convention.
So shuffle a few registers around and admit a call instruction.
That's awesome.
It surprised me when I first got into Python, how expensive calling a function was.
Not regardless of what it does, just the act of calling it.
You know, coming from C# and C++, where you think so good in line by either the compiler or the JIT compiler and all sorts of interesting things.
You're like, wait, this is expensive.
I should consider whether or not I'm calling a function in a tight loop.
There's so many things it has to deal with.
The JIT has to deal with adding the default values then, and you don't know whether you're going to have to do that until you get to the function.
It's got to deal with taking keyword arguments and mapping those onto the correct keywords.
And that's one thing in static Python, we do that at compile time.
If you're calling the keyword arguments, they turn into positional arguments because we know what we're going to.
And we can just shuffle those around at compile time and just save a whole bunch of overhead.
Yeah, that's fantastic.
So the way people should think of this is maybe like mypyC or Cython, where it looks like regular Python, but then out the other side comes better stuff.
Except for the difference here is you guys do it at JIT, not some sort of ahead of time pre-deployment type of thing.
Yeah.
And so the first thing we did with it was actually we had 40 Cython modules that were inside of the Instagram code base.
And that was a big developer pain point and that those things had to be rebuilt.
The tooling for like editing them wasn't as good because you don't get syntax color highlighting.
And so we were able to just get rid of all of those.
And those were heavily tuned, like using a bunch of Cython features.
And so that really kind of proved things out that like, if we need to use low level features, we support things like permanent events if you want to use them.
Instead of like having boxed variable size ends.
So that was a good proving that it worked.
And now I think it's more close to my PyC at runtime as we've been going through and converting other modules to static Python within the Instagram code base.
Yeah, fantastic.
You guys say that static Python plus sender JIT achieves seven times performance improvements over CPython on the type version of Richard's benchmark.
I mean, obviously you got to be specific, right?
But still, that's a huge difference.
Yep.
And some of that's like the ability to use primitive integers.
Some of that's the ability to use b tables for invoking functions instead of having to do the dynamic lookup, which is something that both my PyC and Cython support.
So lots of little things end up adding up a lot.
And so that's just the JIT.
Yeah, that's fantastic.
Talk Python to me is partially supported by our training courses.
We have a new course over at Talk Python.
HTMX plus Flask.
Modern Python web apps hold the JavaScript.
HTMX is one of the hottest properties in web development today.
And for good reason.
You might even remember all the stuff we talked about with Carson Gross back on episode 321.
HTMX, along with the libraries and techniques we introduced in our new course, will have you writing the best Python web apps you've ever written.
Clean, fast, and interactive.
All without that front-end overhead.
If you're a Python web developer that has wanted to build more dynamic, interactive apps, but don't want to or can't write a significant portion of your app in rich front-end JavaScript frameworks, you'll absolutely love HTMX.
Check it out over at talkpython.fm/HTMX or just click the link in your podcast player show notes.
You've talked about using primitive integers.
And I've always thought that Python should support this idea somehow.
Like, if you're doing some operation, like computing the square root or something, you take two numbers, two integers, and do some math, you know, maybe multiply, you know, square them, and then subtract them or something like that.
And all of that stuff goes through a really high overhead version of what a number is, right?
Like, instead of being a four or eight byte thing on a register, it's 50 bytes or something like that.
As a pi object long thing that gets ref counted, and then, like, somewhere in there is the number bit.
And that's awesome because it supports having huge numbers.
Like, you don't ever see negative 2.1 billion when you're adding.
You increment a number by one in Python, which is great.
But it also means that at certain times you're doing math is just so much slower because you can't use registers.
You've got to use, like, complex math, right?
It sounds like you're doing this, like, let's treat this number as a small number rather than a pi object pointer drive thing.
You know, JITs can handle this to some degree, right?
And they can recognize that things are small numbers and generate more efficient code.
I think when you had Anthony Yanni is talking about Pigeon doing this.
Yeah.
You know, if there's still some overhead there for dealing with the cases where you have to bail out and it's not that case.
It's nice just having the straight line code that's there.
You can also do type pointers, which, again, kind of handle that.
Type pointers are kind of difficult on CPython because things expect pi object stars.
And if that pi object star ever escapes to something that's not your CPython code, it's going to be very unhappy.
Yeah.
So this is, I mean, the nice thing is it's a relatively straightforward way to allow it.
It was actually a little bit controversial in that, like, is this really what Python developers are going to expect?
And are we going to have the right semantics there?
And I think we have a to-do item to actually make things raise overflow errors if they do overflow instead of flowing over to negative 2 billion.
That would be fantastic.
I would personally rather see an overflow error than have it, you know, wrap around to the negative side or go back to zero if it's unsigned or whatever terrible outcome you're going to get there.
Yeah.
It's a much more reasonable behavior.
We just, I guess we haven't been very motivated to actually go and fix that.
Well, you're probably not doing the type of processing that would lead to that, right?
You're probably not doing, like, scientific stuff where all of a sudden, you know, you took a factorial too big or you did some insane thing like that.
There's probably not a single factorial in the entire code base, I would guess.
Yeah.
There's not a lot of math.
There was, like, some, like, the only place where you've used primitive integers really was in the existing conversion, in the conversion of the existing Cython code where people had resorted to them.
Right.
Because it probably started as an int 32 or an int 64, right?
Yeah.
Like, they had that option available to them.
They used it.
It's not, like, something that we're going through and sprinkling in in our random Python code.
Because, like, yeah, we don't do much math.
It's very object-oriented.
Lots of function calls.
Lots of classes.
Yeah.
Absolutely.
All right.
There's a lot of other good things that you talked about that are not necessarily listed right here.
sort of, kind of, stuff with async and await.
It sounds like you guys use async and await a lot.
Is that right?
Yeah.
The entire code base is basically async.
There was a big conversion, a big push to convert it right as I was starting.
And now everything basically is async.
Unless, obviously, it's not as...
Wait a minute.
I heard that async and await is slow.
Why would you ever use that?
Because it allows additional parallelization.
Oh, yeah, yeah.
Because multiple requests can be served by the same worker.
Sure.
Well, you know, whenever I hear those, I see examples of, like, we're just calling something as fast as you can.
And it doesn't really provide...
There's not an actual waiting, right?
Like, the async and await is really good to scale the time.
When you're waiting, do something else.
And a lot of the examples say, well, this is slower.
There's, like, no waiting period.
But you know what is a really good slow thing?
An external API and a database.
And it sounds like you guys probably talk to those things.
And yes.
And the no waiting case is actually what this eager co-routine evaluation is all about.
Like, yeah, sometimes we're talking to a database.
But sometimes you have a function that's like, have I fetched this from the database?
Okay, here it is.
I don't have to wait for it.
Otherwise, I'll go off and fetch it from the database.
Right.
If there's an early return before the first await.
Exactly.
There's not a huge value to calling this, right?
Yeah.
So tell us about this eager co-routine evaluation, which deals with that, right?
Yeah.
So this lets us run the function up to the first await and only go off and kind of.
So normally what happens is you produce your co-routine object, schedule that on your event loop,
and then eventually it'll get called.
And now when you call the function, it's going to run.
It's going to immediately run up to the first await.
And if it doesn't hit that first await, it's just going to have the value that's produced.
And you're not going to have to go through this big churn of going through the event loop with this whole co-routine object.
Yeah, that's fantastic.
Yeah.
It is slightly different semantics because now you could have some CPU heavy thing, which is just like not sharing with CPU with other workers, which is a great.
And I think it can end up kind of, I think there can be some slight differences on what the scheduling happens, like where you could have observable differences, but we haven't had any issues with that.
So I think it's might be a little bit controversial, but it's such a big when that it makes a lot of sense for us.
It certainly could change the order.
If you're doing, here's a whole bunch of co-routines and a bunch of awaits and stuff, and then you ran them in one mode, the sort of standard mode versus this, you would get a different order.
But, you know, I mean, it sounds like you're going to ultimately put the same amount of CPU load on.
I mean, async and await runs on one thread anyway, generally.
Yeah.
Unless you do something funky to like wrap some kind of thread or something, but in general, it still runs there.
I would hope that most people aren't super dependent upon the order.
If you're dependent upon the order and you're doing threading or something like that, you're doing it wrong.
Yeah.
The fairness issue might be a bigger issue.
Yeah, yeah, yeah.
Yeah.
For us, it makes a lot of sense.
Yeah, that's really cool.
All right.
Another one was shadow code or shadow byte code.
Yeah.
So this is our inline caching implementation.
We've had this for a few years.
Python 3.11 is getting something very similar.
So we kind of expect that our version will be going away.
We'll have to see if there's any cases that aren't covered or if there's any performance differences.
But basically, it's nearly identical.
We have an extra copy of the byte code, which is why it's called shadow byte code, which we can mutate in the background and replace the normal opcodes with specialized ones.
So if we're doing a load adder and that load adder is an instance of a specific type, we can just say, okay, well, we know that this load adder doesn't have a type descriptor associated with it.
Descriptor associated with it, like a get set data descriptor.
We know that the instance has a split dictionary, which is the way CPython shares dictionaries, dictionary layout between instances of classes.
We know this attribute is at offset two within split dictionary.
So we just do a simple type check and make sure that the type is still compatible and go off and look in the instance dictionary and pull the value out.
Instead of going through and looking up all those other things that I've just described, which is kind of what you have to do every single time on a normal load adder.
Yeah, that's really cool.
Is this something that could come back to CPython?
I think the fact that they've gone off and built their own version 3.11 means that's not going to happen.
But the idea lives there.
Yes.
Yeah.
Okay.
Awesome.
So we're getting short on time here, but maybe you could just highlight really quickly, stepping back one feature point on the asyncio stuff is the send receive without stop iteration stuff that you did.
And then that getting upstreamed as well already.
Yeah.
So that was adding.
So I did work on this developer Vladimir Mativ worked on this and that was adding in a, I think he added in a new set of slots for actually achieving this at the end of the day.
And sender.
We have a type flag that says this type has these additional slots.
And so we can call the send function and the receive function and get back and be done.
That's kind of did this thing return a result?
Did this thing throw an exception?
And here's the result.
Yeah.
So that instead of producing the stop iteration on every single result, we just return the result.
And that is obviously big with coroutines because coroutines are generators at the end of the day.
Yeah.
That's fantastic.
Everything can get more efficient by not allocating on sort of hidden behind the scene exceptions, right?
Yeah.
All right.
Well, there's a bunch of cool stuff here.
And I'm really happy to hear that you and your team and Edomar out there are working on bringing this stuff over.
Because I was so excited when I saw it.
And then I saw, is it supported?
Like, not really.
You really shouldn't use this.
I'm like, oh, but it looks so good.
Like, I want so much of this stuff to be moved over.
So that's cool.
And I think some of it will be difficult to move over.
Like, in moving the entire JIT over, the JIT's written in C++.
Obviously, the CPython core developers were open to C++ for a JIT at one point in time with unladen swallow.
Whether or not that feeling has changed, who knows.
But it's a big piece of code to drop in.
So one thing that we really want to do going forward is actually get to the point where the big pieces of sender are actually just pip installable.
So we'll work on getting the hooks that we need upstreamed.
One thing that the JIT relies on a lot is dictionary watchers that we can do really super fast global loads.
And we have a bunch of hooks into, like, type modification and function modification that aren't super onerous by any means.
Yeah.
So if we can get those upstream, then we can make the JIT just be, here, pip install us.
And so hopefully we can get those upstreamed in 3.11 and have pip install sender start working.
Yeah.
That'd be awesome.
Yeah.
So, yeah, really good work on these.
I guess let's wrap up our conversation here because we're definitely short on time.
But, you know, there's the other projects, which I'm going to start calling the Shannon Plan that Mark and Guido are working on.
They've been working on for a year.
And then there's Pidgin, which, by the way, Anthony Shaw has taken over, but you created Pidgin, right?
Yep.
That's awesome.
Well done on that.
On a whim at a playtime.
Exactly.
And Sam Gross's work on the NoGill stuff.
All of this seems to be independent, but in the same area as those things.
Where do you see the synergies?
Do you see any chance for those to, like, come together?
Is that through some kind of pip putting the right hooks in there and other people plugging in what they want?
Or what do you see there?
It'd be great if these could come together a little bit.
Yeah.
In a lot of places, we're working on independent things.
Obviously, Pidgin is a JIT and we're a JIT.
With different goals to some degree, right?
Yeah.
But, I mean, also very similar and overlapping goals.
I think there'll probably have to be discussion of, like, what the future of JITs look like in CPython.
Like, is that something that's part of the core?
Or is that something that should live on as being external?
Or is there going to be a grand competition and at one point one of the JITs will win?
Who knows?
It's a good discussion that should probably take place.
The hooks for JITs are there.
And between what Brett and I added for Pidgin and Mark Shannon's Vector Call work that happened several releases ago,
I think JITs have a pretty good foundation for booking in and replacing code execution.
They probably need other books to, you know, get into other things like the Dictionary Watchers that I mentioned.
But, like, we can keep working on books.
Other things have less overlap.
So, hopefully we can all kind of work in our own streets and work to improve things and make those available to Python developers in the best way that's available.
And not be stomping on each other's shoes or do profiting work too much.
Yeah, absolutely.
Well, it's an exciting time.
I feel like a lot of stuff is sort of coming back to the forefront.
And it feels like...
So much performance work.
Yeah, for sure.
It feels like the core developers are open to hearing about it and taking on some of the, you know, the disruption and complexity that might come from it.
But still, it could be valuable, right?
Mm-hmm.
It's absolutely going to be valuable.
Yeah.
I feel like there's enough pressure from other languages like Go and Rust and stuff.
Oh, you should come over to our world and forget that Python stuff.
You're like, hold on, hold on, hold on.
We can just...
We can do that too.
But we've got to...
We can get faster.
Yeah.
Well, this is awesome work.
Thanks for coming on and sharing.
Thank you for having me.
Yeah, you and your team are doing...
Now, before you get out of here, got the final two questions.
Okay.
You're going to write some...
Let's do notable PyPI package first.
So is there some library or notable package out there that you come across?
Like, oh, this thing's awesome.
People should know about whatever.
So does it have to be PyPI?
No, any project.
So as I said, I have a very weird relationship with Python, right?
As using mainly from the implementation side.
So I think my favorite package is the standard library.
Okay.
Right on.
And if I had to pick something out of the standard library, I think one of the coolest parts is
mock.
It's been an interesting integration with static Python, but like it, like seeing the way people
use it and drive their tests, it's kind of really kind of amazing.
Yeah, I agree.
It's definitely a very cool one people should certainly be using.
And now if you're going to write some Python code, you might also have special requirements
that shift you in one way or the other.
But what editor are you using?
Oh, I use VS Code pretty much.
Well, I use VS Code.
I use nano when I need to make a quick edit from the command prompt.
Yeah, cool.
I'm a fan of nano as well.
Like, let's just keep it simple.
It's just give me a nano.
Let me edit this thing over the shelf.
It has syntax cover highlighting that.
It's so advanced.
It's awesome.
Cool.
No, no, I use it as well.
All right.
Well, Dino, thank you so much for being here.
Final call to action.
People are excited about these ideas.
Maybe they want to contribute back or try them out.
What do you say?
I mean, try out sender.
Yeah, it's unsupported.
But, you know, if you have thoughts on it, that's cool.
You do have instructions on how to build it right here.
So you could check it out.
There's a Docker container.
Yeah.
Okay.
Yeah.
So it's pretty easy to give it a shot.
You know, like, it might be harder to get it up and running in a perf-sensitive environment.
If you want to try out Static Python, that'd be cool.
Or Strict Modules.
And give us any feedback you have on those.
Fantastic.
All right.
Well, thanks for being on the show.
Great to chat with you.
Thank you, Michael.
Yeah, you bet.
Bye.
See ya.
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.
Take some stress out of your life.
Get notified immediately about errors and performance issues in your web or mobile applications with
Sentry.
Just visit talkpython.fm/sentry and get started for free.
And be sure to use the promo code TALKPYTHON, all one word.
With TopTal, you get quality talent without the whole hiring process.
Start 80% closer to success by working with TopTal.
Just visit talkpython.fm/TopTal to get started.
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'll see you next time.
Thank you.