Python performance has come a long way in recent times, and it's often the data scientists,
with their computational algorithms and large quantities of data, who care the most about
this form of performance.
It's great to have Stan Siebert back on the show to talk about Python's performance for
data scientists.
We cover a wide range of tools and techniques that will be valuable for many Python developers
and data scientists.
This is Talk Python to Me, episode 474, recorded July 18th, 2024.
Are you ready for your host?
Here he is.
You're listening to Michael Kennedy on Talk Python to Me.
Live from Portland, Oregon, and this segment was made with Python.
Welcome to Talk Python to Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython,
both accounts over at fosstodon.org.
And keep up with the show and listen to over nine years of episodes at talkpython.fm.
If you want to be part of our live episodes, you can find the live streams over on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube and get notified about upcoming
shows.
This episode is sponsored by Posit Connect from the makers of Shiny.
Publish, share, and deploy all of your data projects that you're creating using Python.
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quattro, Reports, Dashboards, and APIs.
Posit Connect supports all of them.
Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T.
And it's also brought to you by us over at Talk Python Training.
Did you know that we have over 250 hours of Python courses?
Yeah, that's right.
Check them out at talkpython.fm/courses.
Hey, Stan.
Hello.
Hello, hello.
Welcome back to Talk Python to Me.
I'm glad to be here.
Glad to talk performance.
I know.
I'm excited to talk performance.
It's one of those things I just never get tired of thinking about and focusing on.
It's just so multifaceted.
And as we will see, even for a language like Python that is not primarily performance focused,
there's a lot to talk about.
Yeah, there's endless bag of tricks.
Yeah.
And I would say, you know, sort of undercut my own comment there.
Python is increasingly focusing on performance since like 3.10 or so, right?
And the secret is that it's because Python integrates so well with other languages.
It's sort of always cared about performance in some way.
It's just sometimes you had to leave Python to do it.
But you've still got to keep the Python interface.
There's been such an easy, high-performance escape hatch that making Python itself faster
is obviously not unimportant, but maybe not the primary focus, right?
Like usability, standard library, et cetera, et cetera.
All right.
For people who have not heard your previous episode, let's maybe just do a quick introduction.
Who's Stan?
Yeah.
So I am Stan Siebert.
I am a manager at Anaconda, well-known purveyor of Python packages and such.
My day-to-day job is actually managing most of our open source developers at Anaconda.
So that includes the Numba team.
And so we'll be talking about Numba today, but other things like we have people working
on Jupyter and Beware for Mobile Python and other projects like that.
And so that's what I do mostly is focus on how do we have an impact on the open source
community and what does Python need to sort of stay relevant and keep evolving?
I love it.
What a cool job as well, position in that company.
Yeah, I'm really grateful.
It's a rare position.
So I'm really glad I've been able to do it for so long.
Yeah.
And I would also throw out in the list of things that you've given a shout out to, I would
point out PyScript as well.
Oh, yes, of course.
Yeah.
I just started managing the PyScript team again, actually.
And so I forgot about that one too.
Yes, PyScript.
So Python in your web browser, Python everywhere, on your phone, in your browser, all the places.
Yeah.
I mean, it's a little bit out of left field compared to the other things that you all are
working on, but it's also a super important piece, I think.
So yeah, really cool.
Really cool there.
So I think we set the stage a bit, but maybe let's start with Numba.
That one's been around for a while.
Some people know about it.
Others don't.
You know, what is Numba and how do we make Python code faster with Numba?
Yeah.
So there have been a lot of Python compilation projects over the years.
Again, Numba's very fortunate that it's now 12 years old.
We've been doing it a long time, and I've been involved with it probably almost 10 of those
years now.
And Python, I think one of Numba's success points is trying to stay focused on an area where
we can have a big impact, and that is trying to speed up numerical code.
So there's a lot of, again, in data science and other sciences, there's a lot of need to
write custom algorithms that do math.
And Numba's sweet spot is really helping you to speed those up.
So we see Numba used in a lot of places where maybe the algorithm you're looking for isn't
already in NumPy or already in JAX or something like that.
You need to do something new.
Projects like UMAP, which do really novel sort of clustering algorithms.
Or I just at SciPy, I learned more about a project called Stumpy, which is for time series analysis.
Those authors were able to use Numba to take the numerical core of that project that was
the sort of the time bottleneck and speed it up without having to leave Python.
And so that is, I think, really where Numba's most effective.
Sure.
If you look at a lot of programs, there might be 5,000 lines of code or more, but even just
something only as big as 5,000 lines.
There's a lot of code, but only a little bit of it really actually matters, right?
Yeah, that's what we find a lot is when you sit down and measure your code, you'll spot
some hotspots where 60 or 70 or 80% of your time is spent in just like three functions or
something.
And that's great.
And if that's the case, that's great because you can just zero in on that section for speeding
things up and not ruin the readability of the rest of your program.
Sometimes optimization can make it harder to read the result.
And so there's always a balance of you have to keep maintaining this project.
You don't want to make it unreadable just to get 5% more speed.
Yeah, absolutely.
Not just the readability, but the ability to evolve it over time, right?
So maybe it's like, oh, we're going to compile this section here using Numba or Cython or something
like that.
Well, maybe I was going to use this cool new IPI package I found, but I can't just jam it
in there where it's compiled.
That's unlikely to work well, right?
And things like that.
And so, yeah, a lot of times there's these big sections that look complicated.
They look slow.
They're not actually.
Yeah.
And one thing I also often emphasize for people is that when you think about the time your
program takes, think about the time you spent working on it as well as the time you spent
running it.
And so because we've heard from a lot of projects who said they were able to get major speed
ups, not because necessarily because Numba compiled their code to be incredibly fast,
but it compiled it to be fast enough that they could try new ideas quicker.
And so they got to the real win, which was a better way to solve their problem because
they weren't kind of mired in just kind of boilerplate coding for so long.
Right, right, right.
It turns out I learned I should use a dictionary and not a list.
And now it's a hundred times faster.
And that wasn't actually a compiling thing.
That was a visibility thing or something, right?
Yeah.
Try more things is always helpful.
And so something that a tool that lets you do that is really valuable.
A hundred percent.
So what tools do you recommend for knowing?
Because our human intuition sometimes is good, but sometimes is really off the mark in terms
of thinking about what parts are slow, what parts are fast.
That's something I definitely want.
I've talked to people.
Everyone thinks they know where the heart, the slow part is, but sometimes they're surprised.
And so you definitely before you do anything, it does.
This is not just number advice.
This is any time before you're going to speed up your program, measure something.
So what you want is you want a representative benchmark, something that's not going to run
too fast because often, you know, like unit tests run too quickly to really tell you to
exercise the program in a realistic way.
So you want a benchmark that doesn't run too long, but maybe like five minutes or something.
And then you're going to want to run that through a profiling tool.
And there are several options.
I just usually tell people to use C profile.
It's built into the standard library in Python.
It's a great tool.
It does the job for most stuff.
And so sometimes you may, there may be other tools, things like snake vis and other things
to help you interpret the result of the profile.
But often you'll use C profile to collect the data.
And what this does is it samples, it sort of records as the program is running, what are
all the functions that are being called and how much time are they taking?
And there are different strategies for how to do this, but fundamentally what you get out
is a essentially a data set that says, you know, 2% of the time in your, in your, in
your program, this function was running and 3% of this function was running.
And you can just sort that in descending order and look and see where, what pops out at the
top.
And sometimes you're surprised.
Sometimes you find out it's actually, it wasn't my numerical code.
It's that I spent, you know, 80% of my time doing some string operation that I didn't
realize it needed to do over and over again.
Right, right.
Exactly.
Some weird plus equals with a string was just creating a thousand strings to get to
the end point or something like that.
Yeah.
And it could have just done that once upfront.
It's good to do the profiling just to make sure there isn't an obvious problem before you
get into the more detailed optimization.
Yeah.
Before you start changing your code completely, it's execution method or whatever.
Yep.
Yeah.
And, you know, shout out to the PyCharm folks.
They've got to push the button to profile and they've got to visualize it and they just run
C profile right in there.
So that's like C profile on easy mode.
You know, you get a spreadsheet and you get a graph.
What about other ones like Bill, FIL or anything else?
Like any other recommendations?
Yeah.
So that's an interesting point is C profile is for compute time profiling.
An interesting problem you run into is this tool does, which is data is memory profiling,
which is often a problem when you're scaling up.
And that's actually one of the other good things to keep in mind when you're optimizing is what
am I trying to do?
Am I trying to get done faster?
Am I trying to save on compute costs?
Am I trying to go bigger?
And so I have to speed things up so that I have room to put more data in.
If that's where you're going, you might want to be out of memory.
Right.
Can I just not do?
Yeah.
Or is that am I already stuck?
And so there it is very easy in Python to not recognize when you have temporary arrays and
things.
Because again, it's also very compact and you're not seeing what's getting allocated.
You can accidentally blow up your memory quite a lot.
And so this kind of a profiler is a great option for it.
What it can often show you is they'll kind of a line by line.
This is, you know, how much memory was allocated in each line of your program.
So you can see, oh, that one line of pandas.
Oops.
That that did it.
Yeah, I can't remember all the details.
I talked to it more about this one, but I feel like it also keeps track of the memory
used even down into like NumPy and below, right?
Not just Python memory where it says now there's some opaque blob of data science stuff.
Yeah.
And actually, even on the compute product, there's sort of two approaches.
So C profile is focused on counting function time.
But sometimes you have a long function.
And if you're making a bunch of NumPy calls, you might actually care line by line how much time
is being taken.
And that can be a better way to think about it.
And so I think the tool is called LineProf.
Yeah.
I forget the exact URL, but it's an excellent tool in Python for there's one in R and there's
an equivalent one.
Yes.
Robert, Robert, LineProfiler.
There you go.
LineProfiler.
Oh, it's archived, but still can be used.
Yeah.
I have to find another tool now.
This is my go-to for so long.
I didn't realize it had already been archived.
Oh, there's a copy.
Hey, but it still works.
It's all good.
It's been transferred to a new location.
So that's where it lives now.
But yeah, line profiling is another.
I often use them complimentary sort of tools.
As I zero in on one function that's with C profile, and then I'll go line profile that
function.
Oh, interesting.
Yeah.
Okay.
Drilling further.
Yeah.
Okay.
This is the general area.
Now let's really focus on it.
Memory is another one.
I talked to the folks from Bloomberg about that.
Oh, okay.
I have not used this one.
Yeah.
This is a pretty new one.
And it's quite neat the way it works.
Yeah.
This one actually tracks C and C++ and other aspects of allocations as well.
So one of the problems you can run into with profiling is especially memory profiling, I think.
Although if you just want to know about memory, but the more you monitor it, the more it becomes
kind of a Heisenberg quantum mechanics type thing.
Once you observe it, you change it.
And so the answers you get by observing it are not actually what are happening.
So you got to keep a little bit of an open mind towards that as well, right?
Yeah.
And that's even a risk with, you know, other the compute side of the profiling is some you're
using some compute time to actually observe the program, which means that it can.
And these tools try to subtract out that bias, but it does impact things.
And so you may want to have kind of a benchmark that you can run as your kind of real source of
truth that you run without the profiler turned on just to see a final runtime run with the
profiler to break it down.
And then when you're all done, you're going to want to run that program again with the profiler
off to see if you've actually improved it while clock time wise.
Yeah.
Yeah, absolutely.
That's a really good point.
It's just maybe do a percent time it type of thing or something along those lines.
Okay.
That was a little bit of a side deep dive into profiling because you, before you apply
some of these techniques like Numba and others, you certainly want to know where to
apply it.
And part of that is you might need to rewrite your code a little bit to make it more optimizable
by Numba or these things.
So first of all, like what do you do to use Numba, right?
It's just, you just put a decorator on there and have you go.
At the very simplest level, Numba's interface is supposed to be just one decorator.
Now there's some nuance, obviously, and other things you can do, but we tried to get it down
to for most people, it's just that.
And the N in NGIT means no Python, meaning we get, this code is not calling the Python
interpreter anymore at all.
It is purely machine code, no interpreter access.
Interesting.
Okay.
So some of these like compile, do a thing and compile your Python code to machine instructions.
I feel like they still interact with like Py object pointers and they still kind of work
with the API of the Python data types.
Yeah.
Which is nice, but it's, it's, it's, it's a whole, whole lot slower of an optimization
than now it's in 32 and it's, you know, float 32 and these are on registers, you know?
Yeah.
And this is part of the reason why Numba focuses on numerical code is that NumPy arrays and actually
other arrays and PyTorch and other things that support the buffer protocol.
The API.
So, so really when Numba compiles this, it compiles sort of two functions.
One is a wrapper that handles the transition from the interpreter into no Python land, as we call
it, and then there's the core function that is kind of like you could have written in C
or Fortran or something.
And that wrapper is actually doing all the Py object stuff.
It's reaching in and saying, ah, this, this integer, I'm going to pull out the actual number
and oh, this NumPy array, I'm going to reach in and grab the data pointer and pass those down
into the core where the actual, all the math happens.
So the only time you interact with the interpreter is really at the edge.
And then once you get in there, you try not to touch it ever again.
Now, Numba does have a feature that we added some years ago called an object mode block.
Which lets you in the middle of your no Python code, go back and actually start talking to
the interpreter again.
Right.
Maybe use a standard library feature or something.
Yeah.
The most common use we've seen is you like you want a progress bar to update or something
that's not in your, you know, hot loop.
You don't want, you don't want to be going back to the interpreter in something that's
really performance critical.
But inside of a function, you might have parts that are more or less, you know, one out
of a million iterations.
I want to go update the progress bar or something that's totally valid.
And you can do that with Numba.
There's a way to get back to the interpreter if you really need to.
Okay.
Yeah.
And it says it takes and translates Python functions to optimize machine code at runtime,
which is cool.
So that makes deploying it super easy.
And you don't have to have like compiled wheels for it and stuff.
Using industry standard LLVM compilers.
And then similar speeds to Cs in Fortran.
Yeah.
Which is awesome, but also has implications if I can speak.
For example, when I came to Python, I was blown away that I could just have integers as big
as I want.
If I keep adding to them, they just get bigger and bigger.
Like billions, bazillions of, you know, bits of accuracy.
And I came from C++ and C# and where you explicitly said it's an N32, it's an N64,
it's a double.
And these all had ranges of valid numbers.
And then you've got weird like wraparounds.
Maybe you create an unsigned one so you can get a little bit bigger.
I suspect that you may fall victim or be subjected to these types of limitations without realizing
them in Python if you at NGIT it because you're back in that land, right?
Or does it do you guys magic to allow us to have big?
We do not handle the big integer, which is what you're describing as sort of that integer
that can grow without bound.
Because our target audience is very familiar with NumPy.
NumPy looks at numbers sort of the way you're described from C++ and other languages.
The D type and all that stuff, right?
Yeah.
NumPy arrays always have a fixed size integer and you get to pick what that is, but it has
to be 8, 16, 32, 64.
Some machines can handle bigger, but that it is fixed.
And so once you've locked that in, you can't over, if you go too big, you'll just wrap around
and overflow.
Yeah.
So that limitation is definitely present again in Numba, but fortunately NumPy users are already
familiar with thinking that way.
So it isn't an additional constraint on them too much.
This portion of Talk Python to Me is brought to you by Posit, the makers of Shiny, formerly
RStudio, and especially Shiny for Python.
Let me ask you a question.
Are you building awesome things?
Of course you are.
You're a developer or a data scientist.
That's what we do.
And you should check out Posit Connect.
Posit Connect is a way for you to publish, share, and deploy all the data products that you're
building using Python.
People ask me the same question all the time.
Michael, I have some cool data science project or notebook that I built.
How do I share it with my users, stakeholders, teammates?
Do I need to learn FastAPI or Flask or maybe Vue or React.js?
Hold on now.
Those are cool technologies, and I'm sure you'd benefit from them, but maybe stay focused
on the data project.
Let Posit Connect handle that side of things.
With Posit Connect, you can rapidly and securely deploy the things you build in Python.
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, ports, dashboards, and APIs.
Posit Connect supports all of them.
And Posit Connect comes with all the bells and whistles to satisfy IT and other enterprise
requirements.
Make deployment the easiest step in your workflow with Posit Connect.
For a limited time, you can try Posit Connect for free for three months by going to talkpython.fm
slash posit.
That's talkpython.fm/P-O-S-I-T.
The link is in your podcast player show notes.
Thank you to the team at Posit for supporting Talk Python.
And then one thing you said was that you should focus on using arrays.
Yes.
And that kind of data structures before you apply Numba JIT compilation to it.
Does that mean list as in bracket bracket or these NumPy type vector things?
We all have different definitions.
Yes, that's true.
What are an array?
Generally, yeah, usually the go-to I talk to about is a NumPy array.
So it has a shape.
The nice thing, you know, NumPy arrays can be multidimensional.
So you can represent a lot of complex data that way.
But within an array, there's a fixed element size.
That element could be a record.
So if you want for every cell in your array to store maybe a set of numbers or a pair of
numbers, you can do that with custom D types and things.
And Numba will understand that.
That's the ideal data structure.
Numba does have, we added support a couple years ago for other data structures because
the downside to a NumPy array is that it's fixed size.
Once you make it, you can't append to it like you can a Python list.
So Numba does have support for both what we call typed lists and typed dictionaries.
So these are sort of special cases of lists and dictionaries in Python where the, in the
case of a list, every element in the list has the same type.
Or in the case of a dictionary, the keys are all the same type and the values are all the
same type.
Right.
And those cover a lot of the cases where, you know, when users want to make things where
they don't know how long it's going to be, you're going to append in the algorithm.
A list is a much more natural thing than an umpire array where you might like over allocate
or something that seems.
And dictionaries, our dictionary implementation is basically taken straight from CPython's dictionary
implementation.
So it's very tuned and very fast in the same way CPython's is.
We just had to modify it a little bit to add this type information, but it's really good
for kind of look up random items kind of stuff.
So those are available as additional data structures in addition to the array.
To use those, I would say from number import and something like this.
There are new type in the, in the docs, I'll show you, you can sort of import a typed list
as a special class that you can create.
The downside, by the way, is that, and the reason we have those and we don't just take
historically number used to try and let you pass in a Python list is that wrapper function
would have to go recursively through the list of list of lists of whatever you might have
and pop out all of the elements into some format that wasn't all Py objects so that the no Python
code could manipulate them quickly.
And then how do you put it all back if you modify that sort of shadow data structure?
And so what we realized is that was confusing people and actually added a lot of overhead
and calling functions took too long.
So we instead went up a level and said, okay, we're going to make a new kind of list that
you at the interpreter level can opt into for your algorithm.
And so accessing that list from Python is slower than a Python list, but accessing it from
Numba is like a hundred times faster.
So you kind of have to decide for the, while I'm in this mode, I'm optimizing for numbers
performance, not for the Python interpreter performance.
Which is reasonable often, I'd imagine, because this is the part you found to be slow.
Yeah, that's the trade-off you make.
And so, yeah.
So we would not suggest people use type lists just in random places in their program.
It's really intended to be used.
Yeah.
I heard this is fast.
So we're just going to replace all like new rule.
Bracket, bracket is disallowed.
We're now using this one, right?
Yeah.
When you're working with Python objects, Python's data structures can't be beat.
They are so well-tuned that it's very, very hard to imagine something that could be faster
than them.
All right.
So maybe one more thing on Numba we could talk about is, so far, I imagine people have
been in their mind thinking of, I have at least, running on CPUs, be that on Apple Silicon or
Intel chips or AMD or whatever.
But there's also support for graphics cards, right?
Yes.
Yeah.
So for a very long, I mean, we've had this again for 10 plus years.
We were very early adopters of CUDA, which is the programming interface for NVIDIA GPUs.
CUDA is supported by every NVIDIA GPU, whether it's a low-end gamer card or a super high-end
data center card.
They all support CUDA.
So that was really nice for people who were trying to get into GPU programming.
You could use inexpensive hardware to learn.
And so on both Windows and Linux, Macs don't have NVIDIA GPUs for a long, long time now,
but on Windows and Linux, you can basically write what they call a CUDA kernel in pure Python.
And it just, you know, and you can pass up, you know, arrays, either NumPy arrays, which
then have to be sent to the card or special GPU arrays that are already on the card.
That is a great way for people to learn a bit more about GPU programming.
I will say Numba might not be the best place to start with GPU programming in Python because
there's a great project called Coupy, C-U-P-Y, that is literally a copy of NumPy, but does
all of the computation on the GPU.
And Coupy works great with Numba.
So I often tell people, if you're curious, start with Coupy, use some of those NumPy functions
to get a sense of, you know, when is an array big enough to matter on the GPU, that sort of
thing.
And then when you start wanting to do more custom algorithms, Numba is where you kind of turn
to for that second level.
Yeah.
So I feel like I'm referencing a lot of Etamar's work over here.
But what if we didn't have a NVIDIA GPU?
Is there anything we could do?
Yeah.
So there are other projects.
So things like, as I mentioned here, like PyTorch and things are, have been ported to a
number of different backends.
Well, this is one thing the Numba team, we are frequently talking about is how do we add
non-GPU or non-NVIDIA GPU support?
But it's, I don't have an ETA on that.
That's something that we just still are kind of thinking about.
But PyTorch, definitely.
And you can use PyTorch as an array library.
You don't have to be doing machine learning necessarily.
You can use it just for fast arrays.
It's just most popular for, because it supports, I mean, JAX is a very similar thing, because
it adds the extra features you want for those machine learning models.
But at the core of every machine learning model, it's just array math.
And so you could choose to just do that if that's what you want.
And then you could even still pass those arrays off to Numba at some point in the future.
I'm interested.
Yeah, I didn't realize there was integration.
Yeah, I didn't realize there was integration with that as well.
Yeah.
A while back, we kind of worked with a number of projects to define a GPU array interface
that's used by a number of them so that we can see each other's arrays without having
to copy the data, which is very helpful.
Yeah.
We have a lot more topics than Numba, but I'm still fascinated with it.
Oh, yeah.
So, you know, one of the big, all the rages, all the rage now is vector databases, obviously,
because I want to query my way through LLM outputs.
Right?
Like, where in 100,000 dimensional space does this question live?
Yep.
Or whatever.
Is there any integration with that kind of stuff back into Numba?
Numba, not directly.
Although Numba does have interfaces, an easy way to call out to C functions.
So a lot of these vector databases are implemented in, you know, C or C++ or something.
And so if you did have a use case where you needed to call out to one of them, if there
was a C function call to make directly to sort of the underlying library that bypassed the
interpreter, you can do that from Numba.
And so I haven't seen anyone do that yet, but it's a generic sort of C interface.
Yeah.
Maybe there's a database driver written in C, in which case, I don't know all the different
databases.
I know there are some that are specifically built for it.
Maybe DuckDB has got something going on here, but also MongoDB has added vector stuff to
it.
And I know they have a C library as well.
Yeah.
I've looked at LanceDB is one I've seen mentioned by used by a couple of projects.
That's just for vector stuff.
It doesn't do anything else.
LanceDB.
Okay.
I heard about it in the context of another Python LLM project.
Cool.
Well, that's news to me, but it is a developer-friendly open source database for AI.
Okay.
Brilliant.
All right.
Well, like I said, we have more things to talk about.
So many things.
But this is super.
Okay.
One more thing I want to ask you about here before we go on.
This has a Cython feel to it.
Can you compare and compress Numba to Cython?
So Cython uses sort of requires you to put in some type information in order to be able
to generate C code that is more efficient.
Numba is mainly focused on type inference.
So we try to figure out all the types in your function based on the types of the inputs.
And so in general, we, although Numba has places where you can put type annotations, we
generally discourage people from doing it because we find that it adds work and is error prone
and doesn't really help the performance in the end.
Numba will figure out all the types directly.
And so when it compiles it, if it comes in, if you call it twice with different types,
does it just say, well, now we're going to need a, this version of the function underscore
strings list rather than integers list or something.
Yeah.
Every Numba compiled function actually contains a dispatcher that will look at the argument
types and pick the right one.
And it's at pretty high granularity.
You know, for example, people who are familiar with multidimensional arrays and like Fortran and
C know that they lay out the rows and columns in a different order, which has impact
on how you do loops and stuff for, for kind of to maximize locality.
Numba can tell the difference between those two cases and will generate different code for
those two cases.
Wow.
So this is stuff that you as the user don't want to even know.
No, you don't want to worry about that.
That's a whole nother level.
So you were like, okay, well, if it's laid out in this order, it's probably this, it appears
on the L, you know, the local cache for the CPU in this way.
And so if we loop in that direction, we'll like iterate through the cache instead of blow
through it every loop or something like that.
Basically, we want to make sure that LLVM knows when the step size is one.
And that's either on the row or the column access, depending on that.
And because, I mean, compilers in general are magic.
Like we are grateful that LLVM exists because they can do so many tricks at that level.
I mean, because, I mean, this is the same thing that powers clang and other stuff.
So, you know, all of, all of macOS compilers are built on LLVM.
And so we can leverage all of the tricks they've figured out in decades of development.
Yeah, that's cool.
And Python itself is compiled with that, at least on macOS.
I just saw it last night, you know, that I have clang some version, whatever.
When I was just looking at the version for my, my Python, I was compiled with that.
Cool.
Okay.
So we've been on the Numba JIT.
Anthony Shaw wrote an article, Python 3.13 gets a JIT.
And this is a pretty comprehensive and interesting article on what's going on here.
What's your experience with this JIT coming to Python 3.13?
This is, and they've definitely tried to set expectations here, that this first release is really planting a flag.
It's to say, we're going to start building on top of this base.
And so as far as I've seen, the benchmarks for 3.13 are not going to be like the world on fire kind of stuff.
So throwing away RC code and rewriting operating systems and JIT in Python.
But you have to take a first step.
And this is honestly pretty, it impressed me because as a library, we can take a lot of risks and do things that I know we can depend on LLVM.
We can do all sorts of stuff that may be not work for everyone, because if Numba doesn't solve your problem, you just don't use it.
You can just leave it out of your environment.
You don't have to install it.
And so it's easy to kind of us to zero in on just the problems that we're good at and say, if this doesn't solve your problem, just leave us out.
When you actually are putting a JIT into the core interpreter, everyone gets it.
And you have to consider, and Python is so broad that you could grab two Python experts, and they may actually have nothing in common with each other.
But are both equal claim to being experts at using Python, but they might use them in different domains and have different libraries they care about and all of that.
I feel that way when I talk to Candace people.
And I think about me doing like web development APIs and stuff.
I'm like, I think I'm really good at Python, generally, you know, good enough to write good apps.
But then I look at this, I'm like, I don't even really this.
Some of these like expressions that go into the filter brackets.
I'm like, I didn't even know that that was possible or really how to apply, you know, like it's just, it's weird to both feel really competent and understand it, but also have it kind of no idea.
Yeah, and what I think what you're getting at is those are two really different use cases, and they're getting the same JIT and has to work for both of them.
But, you know, combinatorially explode that problem, right?
Yeah, and, you know, all the different hardware.
I mean, Numbus supports a lot of different computers, but not everyone that Python supports.
Like Micro Python?
Yeah, or we don't work on, you know, HP UX or anything like that, necessarily.
Python has an enormous support, range of supported platforms, an enormous set of use cases.
And anything you do is going to affect everyone.
So this approach, which I would say this copy and patch JIT approach is really clever, because one of the, you know, again, Numba has to bring, we build a, you know, custom version of LVM.
It's a little stripped down, but it's mostly still there.
So we have to bring that along for the ride.
That's a heavy, heavy dependency to put on the core interpreter for everyone.
So the clever bit here is they figured out how to have a JIT, but still do all the compiler stuff at build time.
So when you build this copy and patch JIT, you actually need LVM, but only at build time.
And then it can go away.
And so the person who receives the interpreter doesn't need LVM anymore.
And so they basically built for themselves a bunch of little template fragments.
This is the patching part is basically you're saying, I've got a bunch of fragments that implement different opcodes in the bytecode different ways.
And I'm going to string them together.
And then go in and there's a bunch of fill in the blank spots that I can go in and swap in the okay, you get your value from here and then you put it over here and all that.
But the actual machine code was generated by LVM by the person who built Python in the first place.
I see.
It just amazes me this works.
And I'm excited to see where they go with it because it was a clever way to avoid adding a huge heavy dependency to Python.
Let's start to get some of that JIT benefit.
So it looks at some of the common patterns.
I see.
Okay, we're looping over a list of loads or something and replaces that with more native code or something along those lines.
Yeah.
You essentially have a compiler from a bunch of little recipes that are if I do this pattern, sub in this machine code, fill in these blanks, and you just have a table of them.
So it's the challenge there is that there is a combinatorial explosion again of how many different, you know, a full blown compiler like LVM has a bunch of rules.
It's rule based.
And so it's saying, if I see this pattern, I do this replacement and it keeps doing all of this.
And then at the end, it says, okay, now I'm going to generate my machine code from that, those transformations.
If I don't have LVM at runtime, I have to kind of figure out what are the best templates up front, put them in this table.
And then, you know, and so this is where honestly looking at different usage patterns will probably be a huge help is in practice, you can have any sequence of byte codes, but in reality, you're probably going to have certain ones a lot.
And those are the ones you want to focus on.
So I think once we, I don't know, you know, once we start getting this out in the community and start getting feedback on it, I'm curious to see how rapidly it can evolve.
That'll be really interesting.
Yeah.
And this whole copy and patch it is we often hear people say, I'm a computer, I have a computer science degree.
And I think what that really means is I have a software engineering degree in, or I am a software engineering person.
They are not often creating new science, computer science of theories.
They're more like, I really understand how operating system works and programmers and compilers.
And I, I write JSON API, sorry, talk to databases.
This is like true new research out of legitimate computer science, right?
This copy and patch shit.
Yeah.
They mentioned, I mean, they cite a paper from 2021 and in computer science, going from paper to implementation in one of the most popular languages on earth in three years seems pretty fast.
It does seem pretty fast, right?
It definitely seems pretty fast.
And the reason I bring this up is I imagine that dropping it into one of the most popular programming languages with a super diverse set of architectures and use cases will probably push that science forward.
Yeah.
This will be tested in one of the most intense environments you could imagine.
You know, I mean, whatever they did for their, their research paper or their dissertation or whatever, this is another level of putting it into a test and experimentation to put it into Python.
Yep.
Yeah.
Wild.
Okay.
Hopefully this speeds things up.
This is going to be interesting because it just happens, right?
It just happens.
If you have Python 3.13, it's going to be looking at its patterns and possibly swapping out.
This is complimentary to all of the techniques the faster CPython folks have been doing all along for many releases now.
Yeah.
Is they've, they've been looking at other ways to speed up the interpreter without going all the way to a full blown compiler, which this is kind of giving you the final step.
So that's again, another interesting place is how does this compliment those?
You know, I don't know those, those details, but it's another tool in the toolbox to sort of go back to the beginning.
It's speed is about having a bunch of tools and you kind of pick up 5% here and 5% there and you pile up enough 5% and pretty soon you have something substantial.
Yeah, I absolutely.
That was the plan, right?
The faster CPython was to make it multiples of times faster by adding 20% improvements release over release over release and, you know, compounding percentages basically.
The one thing I don't know about this is we've had the specializing adaptive interpreter that was one of those faster CPython things that came along.
You know, is this just the next level of that or is this a replacement for that?
I don't know.
I'm sure people can.
Yeah, I don't know.
I don't know what the, what their roadmap is for that because I think part of this is, this is so new.
I think they got to see how it works in practice before they start figuring out.
I agree.
It feels like an alternative to that specialized and adaptive interpreter, but I don't know.
Maybe some of the stuff they've learned from one made it possible or even as just an extension of it.
Okay.
What do we want to talk about next here?
I think.
You want to talk about threads?
No, let's talk.
I want to talk about, I want to talk about Rust really quick before we talk about it.
Then, because that'll be quick.
And then I want to talk about threads because threads will not be as quick and it's super interesting.
It's been a problem that people have been chipping at for years and years and years, the threads thing.
But what do you think about all this Rust mania?
I mean, it's shown some real positive results, things like rough and pydantic and others,
but it's actually a little bizarrely controversial or maybe not bizarre, non-obviously controversial.
Yeah.
I mean, my take on the Rust stuff is I view it in the same light as when we use C and Fortran
historically, it's just Rust is a nicer language in many ways.
And so being a nicer language means it's certainly, you know, you could have taken any of these things
and rewritten them in C a long time ago and they would have been faster.
You just didn't want to write that C code.
Exactly.
You know what?
We could do this in assembler and you would fly, guys.
Yeah.
So Rust is moving things, Rust lowering the bar to saying, okay, maybe I'll implement the core of my algorithm outside of Python entirely.
It's interesting.
And honestly, I would happily see Rust completely replace C as the dominant extension language in Python.
The trade-off here, and this is one of those things that's sometimes easy to forget, again, because the Python community is so diverse,
is when you do switch something to Rust, you do reduce the audience who can contribute effectively in some cases.
That using Python to implement things has a benefit for the maintainers if it lets them get more contributions, more easily onboard new people.
I hear this a lot actually from academic software where you have this constant rotating students and postdocs and things.
And so how quickly you can onboard someone who isn't a professional software developer into a project to contribute to it is relevant.
And so, but I think it's different for every project.
There are some things like, you know, again, Rust in cryptography makes total sense to me because that's also a very security conscious thing.
You really don't want to be dealing with C buffer overflows in that kind of code.
And so the guarantees Rust offers are valuable also.
Well, and I think that that's also makes sense even outside of just security directly.
You're going to build a web server.
It's a nerve wracking thing to run other people's code on an open port on the Internet.
And so this is better.
One of the things I switched to is I recently switched to Granian for a lot of my websites, which is a Rust ACP server.
It's comparable in performance, slightly faster than other things, but it's way more, it's deviation from its average is way, way better.
So it's just more consistent.
More consistent, but also, you know, like the average, for example, the average, where's the versus third party server?
That's the one I want.
So for against micro WSGI, for example, right, it's six milliseconds versus 17 milliseconds.
Like, whatever.
But then you look at the max latency of 60 versus three seconds.
It's like, oh, wow.
Hold on.
Right.
But the fact it's written in Rust, I think feels it's a little bit of extra safety, all other things being equal.
Right.
And that, I mean, obviously a lot of caveats there.
Yeah.
The actually the interesting point about, and this is not unique to Rust.
This is, again, the same problem with C and other things is that it's a little bit interesting.
On one hand, we're pushing the Python interpreter and JITs and all this other stuff.
At the same time as you're thinking about whether to pull code entirely out of Python.
And it creates a barrier where the JIT can't see what's in the Rust code.
And so if there was an optimization that could have crossed that boundary, it's no longer available to the compilers.
Yeah.
This is a problem the number team has been thinking about a lot because our number one request, aside from, you know, other GPUs, is can number be an ahead of time compiler instead of a just in time compiler?
And we were like, superficially, yes, that's straightforward.
But then we started thinking about the user experience and the developer experience.
And there are some things that you lose when you go ahead of time that you have with the JIT.
And how do you bridge that gap?
Yeah, it gets tricky.
We've been trying to figure out some tooling to try and bridge that.
So we at SciPy, we did a talk on a project we just started called Pixie, which is a sub project of Numba that is trying to, which doesn't have Rust support yet, but that's been one of the requests.
So if you go to github.com/Numba slash Pixie, see if they've indexed it.
Oh, they're perfect.
Okay.
Search engines.
Search engines are pure magic.
They really are.
But yeah.
But Pixie, we gave a talk about it at SciPy.
It's very early stages.
But what we're trying to do is figure out how to, in the ahead of time compilation, whether that's C or Rust or even Numba eventually, capturing enough info that we can feed that back into a future JIT so that the JIT can still see what's going on in the compiled code as kind of a future-proofing.
Yeah, that's cool.
I know some compilers have profiling-based optimization type things.
Like you can compile it with some instrumentation, run it, and then take that output and feed it back into it.
And I don't know if I've ever practically done anything with that, but I'm like, oh, that's kind of a neat idea to like try it.
Let it see what it does and then feed it back.
Is this sort of like that?
Or what do you think?
This is different.
This is sort of, this is basically capturing in the library file.
So you compiled ahead of time to a library, capturing the LLVM bitcode so that you could pull it out and embed it into your JIT, which might have other LLVM bitcode.
So then you can optimize.
You can have a function you wrote in Python that calls a function in C, and you could actually optimize them together.
Even though they were compiled at different times, implemented in different languages, you could actually cross that boundary.
One's like a ahead of time compilation, just standard compilation.
And one is like a JIT thing, but it's like, oh, we're going to click it together in the right ways.
Yeah.
Wow.
Yeah.
Because JITs are nice in that they can see everything that's going on, but then they have to compile everything that's going on.
And that adds time and latency and things.
And so can you have it both ways is that's really what we're trying to do.
It's nice when you can't have your cake and eat it too, right?
Yes.
My cake before my vegetables and it'd be fine.
I said that this Rust thing was a little bit controversial.
I think there's some just, hey, you're stepping on my square of Python space with a new tool.
I don't think that has anything to do with Rust per se.
It's just somebody came along and made a tool that is now doing something maybe in better ways or I don't know.
I don't want to start up a whole debate about that.
But I think the other one is what you touched on is if we go and write a significant chunk of this stuff in this new language, regardless what language it is,
Rust is a relatively not popular language compared to others, then people who contribute to that, either from the Python side, we're like, well, there's this big chunk of Rust code now that I don't understand anything about.
So I can't contribute to that part of it.
And you might even say, well, what about the pro developers or the experienced core developers and stuff?
They're experienced and grow at C in Python, which is also not Rust, right?
Like it's this new area that is more opaque to most of the community, which I think that's part of the challenge.
Yeah.
Some people like learning new programming languages and some don't.
So on some hand, you know, Rust can be this is a new intellectual challenge and it fixes practically some problems you have with C.
In other cases, it's the I wanted to worry about what this project does and not another programming language.
Right, right, right.
Kind of have to look at your communities and decide what's the right tradeoff for you.
Maybe in 10 years, CPython will be our Python and it'll be written in Rust.
You know, I mean, if we move to WebAssembly and like PyScript, Pyodid land a lot, like having that right in there's there's there's a non zero probability, but it's not a high number, I suppose.
Speaking of something I also thought was going to have a very near zero probability, PEP 703 is accepted.
Oh, my goodness.
What is this?
Yeah.
So this was, you know, again, a couple of years ago now, or I guess a year ago, it was finally accepted.
So for since very long time, the Python interpreter has, you know, again, threads are an operating system feature that let you do something in a program concurrently.
And now that all of our computers have, you know, four, eight, even more cores, depending on, you know, what kind of machine you have, even your cell phone has more than one core.
The using those those cores requires you have some kind of parallel computing in your program.
And so the problem is that you don't want, you know, once you start doing things in parallel, you have the potential for race conditions.
You have the two threads might do the same thing at the same time or touch the same data, get it inconsistent, and then your whole program starts to crash and other bad things happen.
So historically, the global interpreter lock has been sort of sledgehammer protection of the CPython interpreter.
But the net result was that threads that were running pure Python code basically got no performance benefits.
You might get other benefits, like you could have one block on IO while the other one does stuff.
And so it was easier to manage that kind of concurrency.
But if you were trying to do compute on two cores at the same time in pure Python code, it was just not going to happen because every operation touches a Python object has to lock the interpreter while you make that modification.
Yeah, you could write all the multi-threaded code with locks and stuff you want, but it's really just going to run one at a time anyway.
Yeah.
A little bit like preemptive multi-threading on a single core CPU, right?
It's weird.
Like I've added all this complexity, but I haven't got much out of it.
The secret, of course, is that if your Python program contained not Python, like C or Cython or Fortran, as long as you weren't touching Python objects directly, you could release the GIL.
And so Python, so especially in the scientific and computing and data science space, multi-threaded code has been around for a long time.
And we've been using it and it's fine.
Dask, you can use workers with threads or processes or both.
And so I frequently will use Dask with four threads.
And that's totally fine because most of the codes in NumPy and Pandas that release the GIL.
But that's only a few use cases.
And so if you want to expand that to the whole Python interpreter, you have to get rid of the GIL.
You have to have a more fine-grained approach to concurrency.
And so this proposal from Sam Gross at Meta was basically one of many historical attempts to kind of make that, get rid of that global interpreter lock.
Many have been proposed and failed historically.
So getting this all the way through the approval process is a real triumph.
At the point where it was really being hotly contested, my maybe slightly cynical take is we have between zero and one more chance to get this right in Python.
Either it's already too late or this is it.
I don't know which it is.
I think there were two main complaints against this change.
Complaint number one was, okay, you theoretically have opened up a parallel compute thing.
So, for example, on my Apple M2 Pro, I have 10 cores.
So I could leverage all of those cores, maybe get a five times improvement.
But single core regular programming is now 50% slower.
And that's what most people do and we don't accept it.
All right.
That's the one of the size, you know, the golectomy and all that was kind of in that realm, I believe.
The other is yet to be determined, I think, is much like the Python 2 to 3 shift.
The problem with Python 2 to 3 wasn't that the code of Python changed.
It was that all the libraries I like and need don't work here.
Right.
And so what is going to happen when we take half a million libraries that were written in a world that didn't know or care about threading and are now subjected to it?
Yeah.
And there's sort of two levels of problem there.
There's one that there's work that has to be done to libraries.
It's usually with C extensions that, you know, didn't assume that, you know, they assumed a global interpreter lock and they'll have to do some changes to change that.
But the other one is a much more kind of cultural thing where the existence of the guild just meant that Python developers just wrote less threaded code.
Yeah.
They don't think about locks.
They don't worry about locks.
They just assume it's all going to be fine.
Because, again, the race condition doesn't protect threaded code from from the guild.
The guild doesn't protect thread code from race conditions, but it just protects the interpreter from race conditions.
So you and your application logic are free to make all the thread mistakes you want.
But if no one ever ran your code in multiple threads, you would never know.
And so we're going to have to face that now.
I think that's a super interesting thing that it's a huge cultural issue that people don't think about it.
Like I said, I used to do a lot of C++ and C#.
And over there, you're always thinking about threading.
You're always thinking about, well, what about these three steps?
Does it go into a temporarily invalid state?
Do I need to lock this?
Right.
And like C# even had literally a keyword lock, which is like a context manager.
You just say lock curly brace and everything in there is like into a lock and out of like, because it's just so part of that culture.
And then in Python, you kind of just forget about it and don't worry about it.
But that doesn't mean that you aren't taking multiple, like five lines of Python code.
Each one can run all on its own, but taken as a block, they may still get into these like weird states where if another thread after three lines observes the data, it's still busted.
Right.
It's just the culture doesn't talk about it very much.
Yeah.
If no one ever runs your code in multiple threads, all of those bugs are theoretical.
And so it's now what's going to shift is, you know, all of those C extensions will get fixed and everything will be, you know, they'll fix those problems.
And then we're going to have a second wave of everyone seeing their libraries used in threaded programs and starting to discover what are the more subtle bugs.
Do I have global state that I'm not being careful with?
And it's going to be painful, but I think it's necessary for Python to stay relevant into the future.
I'm a little worried.
I mean, one of the common questions we hear is sort of why is this what multiprocessing is fine.
Why don't we do that?
And definitely multiprocessing is big challenge is processes don't get to share data directly.
So either, you know, if even if I have like read only data, I might if I have to load two gigabytes of data in every process and I want to store start 32 of them because I have a nice big computer.
I've just 32 X my data, my memory usage, just so that I can have multiple concurrent computations.
Now, there are tricks you can play on things like Linux, where you load the data once and rely on forking to preserve pages of memory.
Linux does cool copy on write stuff when you fork, but that's like fragile and not necessarily going to work.
And then the second thing, of course, is if any of those have to talk to each other.
Now you're talking about pickling objects and putting them through a socket and handing them off.
And that is, again, for certain kinds of applications, just a non-starter.
Yeah, but then people just start going, well, we're just going to rewrite this in a language that lets us share pointers.
Yeah.
Or at least memory in process.
Yeah.
There's, again, there are a lot of Python users where this, they don't need this, they don't care about this, this will never impact them.
And then there's a whole class of Python users who are desperate for this and really, really want it.
Sure.
You know, my, I think there's a couple of interesting things here.
One, I think that this, I think this is important for stemming people leaving.
I thought, I actually don't hear this that much anymore, but I used to hear a lot of we've left for go because we need better parallelism or we've left for this performance reason.
And I don't know, that's just a success story of the faster CPython initiative or all the people who had been around and decided they needed to leave.
They're gone.
And they, we just don't hear them anymore because they left.
It's like, you know, I used to hear them say this at the party, but then they said they're going to leave.
And I don't hear anyone say they're leaving.
Well, it's because everyone's still here.
Didn't say that.
I don't know.
But I do think having this as a capability will be important for people to be able to maybe adopt Python where Python was rejected at the proposal stage.
You know, like, should we use Python for this project or something else?
Oh, we need threading.
We need computational threading.
We've got, you know, 128 core.
It's out, right?
And then no one comes and complains about it because they never even started that process, right?
So it'll either allow more people to come into Python or prevent them for leaving for that same argument on some projects.
I think that's a pretty positive thing here.
Yeah, there's, yeah, we don't get to count all of the projects that didn't come into existence because of the global interpreter lock.
It's easy when you're in it to sort of adjust your thinking to not see the limitation anymore because you're so used to routing around it.
You don't even stop and think, oh, man, I got to worry about threads.
You just don't think threads.
I totally agree.
And I'll give people two other examples that maybe resonate more if this doesn't resonate with them.
It's the, what if I said, oh, it's a little bit challenging to write this type of mobile phone application in Python.
Like, well, it's nearly impossible to write a mobile phone application in Python.
So we're not even focusing on that as an issue because no one is, I know, Beware and a few other things, there's a little bit of work.
So I don't want to just, I don't want to like, I'm not trying to talk bad about them.
But as a community, there's not like, it's not a React native sort of thing or a Flutter where there's a huge community of people who are just like, and we could do this.
And then how do we, like, there's just not a lot of talk about it.
And that doesn't mean that people wouldn't just love to write mobile apps in Python.
It's just, it's so far out of reach that it's, it's just a little whisper in the corner for people trying to explore that rather than a big din.
And I think, you know, same thing about desktop apps.
Wouldn't it be awesome if we could not have Electron, but like some really cool, super nice UI thing that's almost pure Python?
It would.
But people were not focused on it because no one's trying to do it, but no one's trying to do it because there weren't good options to do it with.
Right.
And I think the same story is going to happen around performance and stuff with this.
Just to jump in, you know, since I have to talk about the Beware folks, I mean, you've described exactly the reason why we fund the Beware development is because, yeah, if we don't work on that now before people sort of, there's a lot of work that has to do before you reach that point where it's easy.
And so recently the team was able to get sort of tier three support for iOS and Android into CPython 313.
So now we're at the first rung of the ladder of iOS and Android support in CPython.
That's awesome.
Poga and Briefcase, the two components of Beware are really focused again on that.
Yeah.
How do I make apps?
How do I make for desktop and mobile?
And so, but it's, yeah, we ran into people is that they just didn't even realize you could even think about doing that.
And so they just, they never stopped to say, oh, I wish I could do this in Python because they just assumed you couldn't.
And all the people who really needed to like were required to leave the ecosystem and make another choice.
And it will take the same amount of, I was going to say, it takes the same amount of time with this.
Even once threads are possible in Python, it'll take years to shift the perception.
Yeah.
And probably some of the important libraries.
Yeah.
All right.
So I'm pretty excited about this.
I was hoping something like this would come and I didn't know what form it would be.
I said there were the two limitations, the libraries and the culture, which you called out very awesomely.
And then also the performance in the, this one is either neutral or a little bit better in terms of performance.
So it doesn't have that disqualifying killing of the single thread of performance.
The personal taking here, I will say again, because you have to be fairly conservative with CPython because so many people use it.
Is that this will be an experimental option that by default, Python won't turn this on.
You will have Python 3.13 when you get it, we'll still have the global interpreter lock.
But if you build Python 3.13 yourself, or you get another kind of experimental build of it, there's a flag now at the build stage to turn off the GIL.
So this is, in this mode, they decided to make, you know, not have to make double negatives.
This is Python in free threading mode.
And that will be an experimental thing for the community to test, to try out, to benchmark and do all these things for a number of years.
They've taken a very measured approach and they're saying, we're not going to force the whole community to switch to this until it's proven itself out.
Everyone's had time to port the major libraries, to try it out, to see that it really does meet the promise of not penalizing single threaded stuff too much.
Yeah.
Or breaking single threaded code too much.
Yeah.
The steering council is reserving the right to decide when this becomes, or if this becomes the official way for Python, you know, I don't know, 3.17 or something.
I mean, it could be, it could be several years.
And so I just want everyone not to panic.
Yeah, exactly.
This doesn't get turned on in October.
No.
And this is super interesting.
It's accepted.
It only appears in your Python runtime if you build it with this.
So I imagine, you know, some people will build it themselves, but someone will also just create a Docker container with Python built with this.
And you can get the free threaded Docker version or whatever, right?
We've already put out conda packages as well.
So if you want to build a conda environment, you actually, if you jump over to the PyFreeThread page.
Yeah.
Tell people about this.
Yeah.
We didn't make this.
This is the community made this.
The scientific Python community put this together.
And this is a really great resource, again, focused on, you know, that community, which really wants threading because we have a lot of, you know, heavy numerical computation.
And so this is a good resource for things like how do you install it?
So there's a link there on what are your options for installing the free threaded CPython.
You can get it from Ubuntu or PyEnv or conda.
If you go look at the, you know, and you can build it from source.
Or get a container.
Yeah.
So these are, again, this is very focused on the kind of things the scientific Python community cares about.
But, but these are things like, you know, have we ported Cython?
Have we ported NumPy?
Is it being automatically tested?
Which release has it?
And the nice thing actually is pip as of 24.1, I believe, can tell the difference between wheels for regular Python and free threaded Python.
Oh, you can tell by the, there's different wheels as well.
Yeah.
So there's a, you know, Python has always had this thing called an ABI tag, which is just a letter that you stick after the version number.
And T is the one for free threading.
And so now you, a project can choose to upload wheels for both versions and make it easier for people to test out stuff.
So for example, I mean, Cython, it looks like there are nightly wheels already being built.
And so this is, they're moving fast and, you know, definitely.
And our conda, we're also very interested in getting into this as well.
So that's why we built the conda package for free threading.
And we're going to start looking at building more conda packages for these things in order to be able to facilitate testing.
Because I think the biggest thing we want to make sure is if you want to know if your code works, you want the quickest way to get an environment to have some place to test.
And so making this more accessible to folks is a really high priority.
This is cool.
There was something like this for Python 2 to 3, I remember.
It showed like the top 1,000 packages on PyPI.
And how many of them were compatible with Python 3, basically by expressing their language tag or something like that.
Yep.
So this is kind of like that.
It's also like the can I use.
I don't know if you're familiar with that.
Can I use from the web dev world?
Oh, yeah.
Oh, awesome.
Yeah, yeah.
I've seen this.
You're going to say, I want to use this.
I want to use this feature.
And it'll, or, you know, if I want to say web workers or something like that, and then it'll, you can, it'll show you all the browsers and all the versions.
And when were they supported?
And this sounds a little bit like that, but for free threaded Python.
Which, by the way, free threaded Python is the terminology, right?
Not no gil, but free threaded.
That is what they've decided.
I think they're worried about people trying to talk about no, no gil or, I mean, I don't know.
Gilful.
Gilful Python.
Are you running on a gilful?
Yeah.
Oh, my gosh.
Okay.
Interesting.
Now, we have a few other things to talk about, but we don't have really much time to talk about them.
But there was one thing that we were maybe going to talk about a bit with compiling.
You said, you mentioned some talk or something where people were talking about, well, what if we had a static language Python and we compiled it?
And related to that, kind of Mr. Magnetic says, could a Python program be compiled into a binary like a JAR or a, you know, Go app or whatever?
There are other tools that look at that as a, yeah, a standalone executable.
So, yeah, one of the things I just wanted to, you know, shout out a colleague of mine at Anaconda, Antonio Cuny, who is a well-known PyPy developer from long ago.
He's worked on PyPy for 20 years.
He's been working.
And not the package installing thing, but the JIG compiler.
PY, PY.
PY, PY, PY.
Yes.
Yeah.
Sometimes phonetically, like over audio, it's hard to tell.
Yes.
Yeah, yeah.
So he's been thinking about this stuff for a very long time.
His sort of key insight, at least clicked in my head, was that Python is hard to compile because it is so dynamic.
I can, in principle, modify the attributes, like even the functions of a class at any point in the execution of the program.
I can monkey patch anything.
I can do this dynamicness is really great for making kind of magical metaprogramming libraries that do amazing things with very little typing.
But it makes compiling them really hard because you don't get to ever say, okay, this can't ever change.
And so what he's been trying to do with a project called Spy, which he gave a talk on at PyCon 2024, but I think the recordings aren't up yet for that.
And so there isn't a, I don't think there's a public page on it, but he does have a talk on it.
And because I think they've got like the key notes up, the key kind of insight for me for Spy was to recognize that in a typical Python program, all the dynamic metaprogramming happens at the beginning.
You're doing things like data classes, generating stuff and all kinds of things like that.
And then there's a phase where that stops.
And so if we could define a sort of variant of Python where those two phases were really clear, then you would get all of the dynamic expressiveness, almost all the dynamic expressiveness of Python, but still have the ability to then feed that into a compiler tool chain and get a binary.
This is super early R&D experimental work, but I think that's a really great way to approach it because often there's always been this tension of, well, if I make Python standardize.
I make Python statically compilable, is it just, you know, C with, you know, different keywords?
Do I lose the thing I loved about Python, which was how quickly I could express my idea?
And so this is again to our, you know, having your cake and eating it too.
This is trying to find a way to split that difference in a way that lets us get most of the benefits of both sides.
That's pretty interesting.
And hopefully that talks up soon.
That'd be really neat.
Maybe by the time this episode's out, I know the Python videos are starting to roll, like not out on YouTube, but out on, out on the podcast channels.
It would be fantastic to have, here's my binary of Python.
Take my data science app and run it.
Take my desktop app and run it.
I don't care what you have installed on your computer.
I don't need you to set up Python 3.10 or higher on your machine and set up a virtual environment.
Just here's my binary.
Do it as you will.
That's another, I throw that in with the mobile apps and the front end or the desktop apps or the front end Python.
You know, that's another one of those things that it's nobody's pushing towards it.
Not that many people are pushing towards it because there's not that many use cases for it that people are using it for because it was so challenging that people stopped trying to do that.
You know?
Yeah, there's one thing I also, you know, people probably hear me say this too many times, but the most people use apps when they use a computer, not packages or environments.
And so in the Python space, we are constantly grappling with how hard packages and environments are to work with, talk with, you know, decide again, what languages are in, what, you know, do I care about everything or just Python or whatever?
That's all very hard.
But that's actually not how most people interact with the computer at all.
And so it really is one of those things.
Again, this is one of the reasons I'm so interested in Beware is Briefcase is like the app packager.
And the more they can push on that, the more we have a story.
And again, there are other tools that have been around for a long time, but that's just what I think about a lot.
We need to focus on tools for making apps because that's how we're going to share our work with 99% of the earth.
Yeah, 100%.
I totally agree.
And lots of props to Keith Russell McGee and the folks over at Beware for doing that and for you guys supporting that work because it's one of those things where there's not a ton of people trying to do it.
It's not like, well, we're using Django, but is there another way we could do it?
It's basically the same thing, right?
It's creating a space for Python where it kind of, I know there's PyInstaller and PyTor app, but it's pretty limited, right?
Yeah, there's not a lot of effort there.
And so there are a few people who have been doing it for a long time and others are getting more into it.
And yeah, so I just, yeah, I wish that we could get more focus on it because there are tools that just don't get a lot of attention.
Yeah, and they're not very polished and there's so many edge cases and scenarios.
All right, let's close it out.
Let's just find a thought on this little topic and then let you wrap this up for us.
Do you think that's maybe a core developer thing?
I mean, I know it's awesome that Py2App and PyInstaller and PyFreeze are doing their things, that Toga are doing their things to try to make this happen.
But I feel like they're kind of looking in at Python and go like, how can we grab what we need out of Python and jam it into an executable and make it work?
Should we be encouraging the core developers to just go like a Python MyScript --Windows?
And they're out, you get in .exe or something.
I don't know, actually, that would be a great question.
Actually, I would ask Russell that question.
He would have probably better perspective than I would.
At some level, it is a tool that is dealing with a lot of problems that aren't core to the Python language.
And so maybe having it outside is helpful, but maybe there are other things that the core could do to support it.
I mean, again, a lot of it has to do with the realities of when you drop an application onto a system, you need it to be self-contained.
You need, sometimes you have to, you know, do you have to trick the import library to know where to find things and all of that?
That's exactly what I was thinking is, right?
If Python itself didn't require like operating system level fakes to make it think it is, if it could go like, here is a thing in memory where you just import, this is the import space.
It's this memory address for these things, and we just run from the EXE rather than dump a bunch of stuff temporarily on disk, import it, throw it, you know, like that kind of weirdness that happens sometimes.
There is probably definitely improvements that could be made to the import mechanism to support applications.
Yeah, exactly.
Well, we've planted that seed.
Maybe it will grow.
We'll see.
All right, Stan, this has been an awesome conversation.
You know, give us a wrap up on all this stuff, just like sort of find a call to action and summary of what you guys are doing in Anaconda, because there's a bunch of different stuff we talked about that are in this space.
Yeah, I mean, mainly, I would say I would encourage people that if you want to speed up your Python program, you don't necessarily have to leave Python.
Go take a look at some of these tools.
Go, you know, measure what your program's doing.
Look at tools like Numba, but there are other ones out there, you know, PyTorch and Jaxx and all sorts of things.
There are lots of choices now for speed, and so Python doesn't have to be slow.
You just have to sort of figure out what you're trying to achieve and find the best tool for that.
Oh, one other thing I do want to shout out.
I'm teaching a tutorial in a month over at the Anaconda sort of live tutorial system, which will be how to use Numba.
So if something you saw here you want to go deep on, there will be a tutorial I hopefully linked in the show notes or something.
Yeah, I can link that in the show notes.
No problem.
Absolutely.
So I'll be going in.
Is that the high performance Python with Numba?
Yes.
So, yeah, we'll be doing worked examples and you'll get to ask questions and all that stuff.
Cool.
I'll make sure to put that in the show notes so people can check it out.
Mm-hmm.
Cool.
All right.
Well, thanks for sharing all the projects that you guys are working on and just the broader performance stuff that you're tracking.
Yeah, awesome.
Glad to chat.
You bet.
See you later.
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.
This episode is sponsored by Posit Connect from the makers of Shiny.
Publish, share, and deploy all of your data projects that you're creating using Python.
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, Reports, Dashboards, and APIs.
Posit Connect supports all of them.
Try Posit Connect for free by going to talkpython.fm/posit, P-O-S-I-T.
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.
And I'll see you next time.