The debate about whether Python is fast or slow is never-ending.
It depends on what you're optimizing for.
CPU server consumption?
Developer time?
Maintainability?
There are many factors.
But if we keep our eye on the pure computational speed in the Python layer,
then yes, Python is slow.
In this episode, we invite Anthony Shaw back on the show.
He's here to dig into the reasons that Python is computationally slower
than many of its pure languages and technologies, such as C++ and JavaScript.
This is Talk Python to Me, episode 265, recorded May 19, 2020.
Welcome to Talk Python to Me, a weekly podcast on Python,
the language, the libraries, the ecosystem, and the personalities.
This is your host, Michael Kennedy.
Follow me on Twitter, where I'm @mkennedy.
Keep up with the show and listen to past episodes at talkpython.fm,
and follow the show on Twitter via at Talk Python.
This episode is sponsored by Brilliant.org and Sentry.
Please check out their offerings during their segments.
It really helps support the show.
Anthony, welcome back to Talk Python.
Hey, Mike.
It's great to be back.
Yeah, it's great to have you back.
You've been on the show a bunch of times.
You've been over on Python Bytes when you're not featured there.
But, you know, people may know you were on episode 168, 10 Python security holes and how to plug them.
That was super fun with one of your colleagues.
And then 214, dive into the CPython 3.8 source code.
Or just what was new in 3.8.
And then a guided tour of the CPython source code, which I think at the time was also 3.8.
And now we're going to look at the internals of Python again.
I feel like you're becoming the Python internals guy.
Yeah.
Well, I don't know.
There's lots of people who know a lot more about it than I do.
But I've been working on this book over the last year on CPython internals, which has been focused on 3.9.
So, yeah, we've got some stuff to talk about.
Yeah, that's awesome.
And your book started out as a realpython.com article, which I'm trying to define a term that describes what some of these look like.
When I think of article, I think of a three to four page thing.
Maybe it's in depth and it's 10 pages.
This is like 109 pages or something as an article, right?
It was like insane.
But it was really awesome and really in depth.
And so you were partway towards a book and you figured like, well, what the heck?
I'll just finish up this walk.
Yeah, I figured I'd pretty much written a book.
So I might as well put it between two covers.
It was actually a lot.
It was actually a lot of work to get it from that stage to where it is now.
So I think the whole thing's pretty much been rewritten.
There's a way that you explain things in an article that people expect, which is very different to the style of a book.
And also there's stuff that I kind of skimmed over in the article.
I think it's actually about three times longer than the original article.
And it's a lot more practical.
So rather than being like a tourist guide to the source code, it's more about like CPython internals and optimizations and practical tools you can learn as more sort of like advanced techniques.
If you use CPython a lot for your day job to either make it more performant or to optimize things or to make it more stable and stuff like that.
Yeah.
It's really interesting because if you want to understand how Python works and you're, say, the world's best Python developer, your Python knowledge is going to help you a little bit.
But not a ton for understanding CPython because that's mostly, well, C code, right?
And so I think this having this guided tour, this book that talks about that is really helpful, especially for taking people who know and love Python, but actually want to get a little deeper and understand the internals or maybe even become a core developer.
Yeah, definitely.
And if you look at some of the stuff we'll talk about this episode, hopefully, like Cython and mypyC and stuff like that, then knowing C or knowing how C and Python work together is also really important.
Yeah, absolutely.
All right.
So looking forward to talking about that.
But just really quickly, you know, give people a sense of what you work on day to day when you're not building extensions for IDEs, writing books and otherwise doing more writing.
Yeah, so I work at NTT and run sort of learning and development and training for the organization.
So I'm involved in, I guess, like what skills we teach our technical people and our sales people and all of our employees, really.
Yeah, that's really cool.
That sounds like a fun place to be.
Yeah, that's a great job.
Yeah, awesome.
All right.
Well, the reason I reached out to you about having you on the show for this specific topic, I always like to have you on the show.
We always have fun conversations, but I saw that you were doing, were you doing multiple or just this PyCon talk?
Just one.
I was accepted for two, but I was supposed to pick one.
I see.
That's right.
And then PyCon got canceled.
Yeah.
So I was like, well, let's, you know, talk.
We can talk after PyCon after you give your talk.
It'll be really fun to cover this.
And then, you know, we were supposed to share a beer in Pittsburgh and we're like half a world away.
Didn't happen, did it?
Yeah.
Maybe next year.
Yeah.
Hopefully next year.
Hopefully things are back to up and running because I don't know.
To me, PyCon is kind of like my geek holiday that I get to go on.
I love it.
Yeah.
All right.
Well, so just, I guess, for people listening, you did end up doing that talk in an altered sense,
right?
And they can technically go watch it soon, at least maybe by the time this is out.
Yeah, definitely.
It'll be out tonight.
It's going to be on the YouTube channel, the PyCon 2020 YouTube channel.
The organizers reached out to all the speakers and said, if you want to record your talk and
submit it from home, then you can still do that and put them all up on YouTube.
I think that's great.
You know, and there's also a little bit more over PyCon online.
One thing I think is really valuable for people right now is they have the job fair, kind of,
right?
There's a lot of job listings for folks who are looking to get in jobs.
Have you seen the PSF JetBrains survey that came out?
Yes.
In the 2019, it came out just like a few days ago.
Really interesting stuff, right?
Like a lot of cool things in there.
Yeah, definitely.
Yeah.
I love that.
That and the Stack Overflow developer survey.
Those are the two that really, I think, have the pulse correctly taken.
One of the things that was in there I thought was interesting is more than any other category
of people, they said, how long have you been coding?
I don't know if it was in Python or just how long have you been coding, but it was different,
you know, one to three years, three to five, five to 10, 10 to 15.
And then people like me forever, long time, you know, like 20 plus or something.
The biggest bar of all those categories, the biggest group was the one to three years.
Yeah.
Right.
Like by 29% of the people said, I've only been coding three years or fewer.
And I think that that's really interesting.
So I think things like that job board and stuff are probably super valuable for folks just getting
into things.
Definitely.
Yeah.
So really good that they're putting that up and people will be able to check out your
talk.
I'll put a link to it in the show notes, of course, but they can just go to the PyCon 2020
YouTube channel and check it out there.
Yeah.
And check out the other talks as well.
There's some really good ones up already.
The nice thing about this year's virtual PyCon is you can watch talks from your couch.
That's right.
You don't even have to get dressed to go to PyCon.
Just do it in your PJs.
That's right.
It's so much more comfortable than the conference chairs.
That's true.
That's for sure.
Yeah.
Very cool.
I'm definitely looking forward to checking out more of the talks as well.
I've already watched a few.
I wanted to set the stage for our conversation here by defining slow because I think slow is
in the eye of the beholder, just like beauty, right?
Like sometimes slow doesn't matter.
Sometimes computational speed might be slow, but some other factor might be quick.
So I'll let you take a shot at it, then I'll throw in my two cents as well.
Like let's like, what do you mean when you say, why is Python slow?
So when I say, why is Python slow?
The question is, why is it slower than other languages doing exactly the same thing and have
picked on an error?
Right.
So if I had an algorithm that I implemented, say in C, a JavaScript on top of Node and Python,
it might be much slower in Python.
Wall time, like execution time.
Yeah.
Execution time might be much slower in Python than it is in other languages.
And that matters sometimes.
And sometimes it doesn't matter as much.
It depends what you're doing, right?
If you're doing like a DevOps-y thing and you're trying to orchestrate calling into Linux, well,
who cares how fast Python goes?
Probably like the startup time is the most important of all of them.
If you're modeling stuff and you're trying to do the mathematical bits, anything computational,
and you're doing that in Python, then it really might matter to you.
Yeah.
So it was kind of like a question, if we can find out the answer, maybe there's a solution
to it.
Yeah.
Because, you know, you hear this thrown around.
People say Python's too slow and I use this other language because it's faster.
And so I just wanted to understand, like, what is the actual reason why Python is slower
at doing certain things than other languages?
And is there a reason that can be resolved?
Or is it just that's just how it is as part of the design?
Fundamentally, it's going to be that way.
Yeah.
I don't think it is.
I think...
You don't think it's slow?
No, I don't think it's fundamentally has to be that way.
I agree with you.
I think in the research as well, it uncovered it doesn't fundamentally have to be that way.
And in lots of cases, it isn't that way either.
Like there's ways to get around the slowdown, like the causes of slowdown.
And if you understand in what situations Python can be slow, then you can kind of like bypass
those.
Right.
So let me tell a really interesting story that comes from Michael Driscoll's book, Python
Interviews.
So over there, he interviewed, I think it was Alex.
Yeah, Alex Martelli.
And they talked about the history of YouTube, right?
YouTube's built on Python.
And why is that the case?
Originally, there was Google Video, which had hundreds of engineers writing, implementing
Google Video, which is going to be basically YouTube.
But YouTube was also a startup around the same time, right?
And they were kind of competing for features and users and whatnot.
And YouTube only had like 20 employees at the time or something like that, whereas Google
had hundreds of super smart engineers.
And Google kept falling behind farther and farther and not be able to implement the features that
people wanted nearly as quick as YouTube.
YouTube.
And the reason was they were all doing it in C++ and it took a long time to get that written.
And YouTube just ran circles around them with a, you know, more less than a fifth of the
number of people working on it.
So in some sense, like that's a testament of Python speed, right?
But it's not its execution speed.
It's like the larger view of speed, which is why I really wanted to find like what computational
speed is.
Another sense where it may or may not matter is like where you're doing stuff that waits,
right?
Somewhere where asyncio would be a really good option, right?
I'm talking to Redis.
I'm talking to this database.
I'm calling this API.
Like if 95% of your time is waiting on a network response, it probably doesn't matter, right?
As long as you're using some sort of async or something.
But then there's that other part where it's like I have on my computer, I've got six hyperthreaded
cores.
Why can I only use one twelfth of my computational power on my computer if I still write C code,
right?
So there's these other places where it super matters.
Or I just, like you said, there's this great example that we're going to talk about the
in-body problem, modeling like planets and how they interact with each other.
And I mean, just like to set the stage, what was the number for C versus Python in terms
of time, computation time?
To give people a sense, like why did we care?
Like why is this a big enough deal to worry about?
Is it, what is it like 30% slower?
It's a little bit slower.
Yeah.
It's a, for this algorithm, so this is called the end body problem and it's to do with calculating
the orbits of some of the planets in the solar system.
And you just do a lot, a really simple arithmetic operations.
So just adding numbers, but again and again and again.
So millions of times.
Lots of loops, lots of math.
Lots of math, lots of looping.
And in C, this implementation is seven seconds to complete.
And in Python, it's 14 minutes.
That might be a difference that you're needing to optimize away.
That could be too much, right?
Yeah.
I mean, everyone is calculating the orbits of planets as part of their day job.
So yeah.
You know, I honestly, I haven't really done that for at least two weeks.
No, but I mean, it's, it's fundamentally like I'm thinking about like, this is, I think this
undercovers one of the real Achilles heels of Python in that doing math in tight loops is really not super great in pure Python.
Right.
Whether that's planets, whether that's financial calculations or something else.
Right.
But numbers are very flexible, but that makes them inefficient.
Right.
Python is interpreted, which has a lot of benefits, but also can make it much slower as well.
Right.
Yeah.
So I think looking at this particular problem, because I thought it would be a good example,
it shines a bit of a spotlight on one of CPython's weaknesses when it comes to performance.
But in terms of like the loop, the only times you would be doing like a small loop and doing
the same thing over and over again is if you're doing like math work, doing like number crunching,
or if you're doing benchmarks, that's like one of the other reasons.
So like the way that a lot of benchmarks designed to do like computational benchmarks anyway,
is to do the same operation again and again.
So if there is an overhead or a slowdown, then it's magnified to the point where you can see
it a lot bigger.
Yeah, for sure.
I guess one thing to put out there before people run code, it doesn't go as fast as they'd hoped.
So they say that Python is slow, right?
Assuming the code they originally ran was Python like that.
That would be a requirement, I guess, is you probably should profile it.
You should understand what your code is doing and where it's slow.
Like, for example, if you're doing lookups, but your data structure is a list instead of
a dictionary, right?
You could make that a hundred times faster just by switching a date because you're just doing
the wrong type of data structure, the wrong algorithm.
It could be just that you're doing it wrong, right?
So I guess before people worry about like, is it executing too slowly?
Maybe you should make sure that it's executing the right thing.
Yeah, and it's unlikely that your application is running a very small operation, which is
this benchmark again and again, like millions of times in a loop.
And if you are doing that, there's probably other tools you could use and there's other
implementations you can do in Python.
This portion of Talk Python to Me is brought to you by Brilliant.org.
Brilliant's mission is to help people achieve their learning goals.
So whether you're a student, a professional brushing up or learning cutting edge topics,
or someone who just wants to understand the world better, you should check out Brilliant.
Set a goal to improve yourself a little bit every day.
Brilliant makes it easy with interactive explorations and a mobile app that you can use on the go.
If you're naturally curious, want to build your problem solving skills, or need to develop
confidence in your analytical abilities, then get Brilliant Premium to learn something new
every day.
Brilliant's thought-provoking math, science, and computer science content helps guide you
to mastery by taking complex concepts and breaking them into bite-sized, understandable chunks.
So get started at talkpython.fm/brilliant, or just click the link in your show notes.
Another benchmark I covered in the talk was the regular expression benchmark,
which Python is actually really good at.
So this is like the opposite to this particular benchmark.
So just saying that Python is slow isn't really a fair statement,
because, and we'll kind of talk about this in a minute,
but like for other benchmarks, Python does really well.
So its string implementation is really performant.
And when you're working with text-based data,
Python's actually a great platform to use, a great language to use.
The CPython compilers is pretty efficient at dealing with text data.
And if you're working on web applications or data processing,
chances are you're dealing with text data.
Yeah, that's a good example.
Like the websites that I have, like the Talk Python training site,
and the various podcast sites and stuff,
they're all in Python with no special,
incredible optimizations,
other than like databases with indexes and stuff like that.
And, you know, the response times are like 10, 30 milliseconds.
There's no problem.
Like it's fantastic.
It's really, really good.
But there are those situations like this in-body problem
or other ones where it matters.
I don't know if it's fair or not to compare it against C, right?
C is really, really low level,
at least from today's perspective.
It used to be a high level language,
but now I see it as a low level language.
If you do a malloc and free and, you know,
the address of this thing, right,
that feels pretty low level to me.
So maybe it's unfair.
I mean, you could probably get something pretty fast in assembly,
but I would never choose to use assembly code these days
because it's just like I want to get stuff done and maintain it
and be able to have other people understand what I'm doing.
But, you know, kind of a reasonable comparison,
I think, would be Node.js and JavaScript.
And you made some really interesting compare and contrast
between those two environments
because they seem like, well, like, okay, Python,
at least it has some C in their JavaScript.
Who knows what's going on with that thing, right?
Like, you know, what's the story between those two?
Yeah, you make a fair point, which is,
I mean, comparing C and Python isn't really fair.
One is like a strongly typed compiled language.
The other is a dynamically typed interpreted language
and they handle memory differently.
Like in C, you have to statically or dynamically allocate memory
and CPython is done automatically.
Like it has a garbage collector.
There's so many differences between the two platforms.
And so I think Node.js, which is,
so Node.js is probably a closer comparison to Python.
Node.js isn't a language.
It's a kind of like a stack that sits on top of JavaScript
that allows you to write JavaScript,
which operates with things that run in the operating system.
So similar to CPython, like CPython has extensions
that are written in C that allow you to do things
like connect to the network
or, you know, connect to like physical hardware
or talk to the operating system in some way.
Like if you just wrote pure Python and there was no C,
you couldn't do that because the operating system APIs
are C headers in most cases.
Right.
Almost all of them are in C somewhere.
Yeah.
And with JavaScript, it's the same thing.
Like if you want to talk to the operating system
or do anything other than like working with stuff
that's in the browser,
you need something that plugs into the OS.
And Node.js kind of provides that stack.
So when I wanted to compare Python with something,
I thought Node was a better comparison
because like JavaScript and Python,
in terms of the syntax, they're very different.
But in terms of their capabilities, they're quite similar.
You know, they both have classes and functions
and you can use them interchangeably.
They're both kind of like dynamically typed.
The scoping is different and the language is different.
But like in terms of the threading as well,
they're quite similar.
Right.
They do feel much more similar.
But there's a huge difference between how they run,
at least when run on Google's V8 engine,
which basically is the thing behind Node and whatnot,
versus CPython is,
CPython is interpreted and V8 is JIT compiled,
just in time compiled.
Yeah, so that's probably one of the biggest differences.
And when I was comparing the two,
so I wanted to see, okay, which one is faster?
Like if you gave it the same task
and if you gave it the end body problem,
then Node.js is a couple of multiples faster.
I think it was two or three times faster
to do the same algorithm.
And for a dynamically typed language,
you know, that means that they must have some optimizations,
which make it faster.
I mean, if you're running on the same hardware,
then, you know, what is the overhead?
And kind of digging into it, I guess,
in a bit more detail.
So JavaScript has this,
actually there's multiple JavaScript engines,
but kind of the one that Node.js uses
is Google's V8 engine.
So quite cleverly named,
which is all written in...
Only would it be better if it were a V12, you know?
Or an inline six.
I think that's a better option.
Yeah, there you go.
So Google's V8 JavaScript engine is written in C++,
so maybe that's a fair comparison.
But the optimizing compiler is called TurboFan,
and it's a JIT optimizing compiler.
So it's a just-in-time compiler,
whereas CPython is an ahead-of-time or an AIT compiler.
And it's JIT optimizer has got some really clever,
basically sort of algorithms and logic
that it uses to optimize the performance
of the application when it actually runs.
And these can make a significant difference.
Like some of the small optimizations alone
can make 30%, 40% increase in speed.
And if you compare even just V8
compared to other JavaScript engines,
you can see, like,
what all this engineering can do
to make the language faster.
And that's how it got two, three multiples
performance increases,
was to optimize the JIT
and to understand, like,
how people write JavaScript code
and the way that it compiles the code
down into operations.
Then basically, like,
it can reassemble those operations
that are more performant for the CPU
so that when it actually executes them,
does it in the most efficient way possible.
Right.
The difference between a JIT and an AOT
is that the JIT compiler
kind of makes decisions
about the compilation
based on the application
and based on the environment,
whereas an AOT compiler
will compile the application the same
and it does it all ahead of time.
Right.
So you probably have a much more
coarsely-grained set of optimizations
and stuff for an ahead-of-time compiler,
like C++ or something, right?
Like, I've compiled against x86 Intel CPU
with, like, the multimedia extensions
or whatever, right?
The scientific computing extensions.
But other than that,
I make no assumptions,
whether it's multi-core,
highly multi-core,
what its L2 cache is,
none of that stuff, right?
It's just,
we're going to kind of target
modern Intel on macOS
and do it on Windows
and compile that.
Yeah.
So modern CPU architectures
and modern OSes
can really benefit
if you've optimized
the instructions
that you're giving them
to benefit,
like,
the caches that they have
or the cycles
that they've set up
and the sort of
the turbo fan optimizer
for the VA engine
takes a lot of advantage
of those things.
Yeah.
That seems really powerful.
I guess we should step back
and talk a little bit
about how CPython runs,
but being an interpreter,
it can only optimize so much.
It's got all of its byte codes
and it's going to go through
its byte codes
and execute them,
but saying, like,
well, these five byte codes,
we could actually turn that
into an inline thing over here
and I see this actually
has no effect
on what's loaded on the stack,
so we're not going to, like,
push the item.
I mean,
it seems like it doesn't operate
optimizing,
tell me if I'm wrong,
if it doesn't optimize,
like, across lots of byte codes
as it's thinking about it.
Yeah, so what CPython will do
when it compiles your code,
and it's also worth pointing out
that when you run your code
for the first time,
it will compile it,
but when you run it again,
it will use the cached version,
so...
Right, if you ever see
the dunder pycache
with .pyc files,
that's, like,
three of the four steps
of getting your code ready to run
saved and done
and never done again.
Yeah, so that's, like,
the compiled version.
So it's not...
If Python is slow
to compile code,
it doesn't really matter
unless your code
is somehow changing
every time it gets run,
which I'd be worried about.
You have bigger problems.
Yeah, exactly.
So the benefits, I guess,
of an AOT compiler
is that you compile things
ahead of time
and then when they execute,
they should be efficient.
So CPython's compiler
will kind of take your code,
which is, like,
a text file,
typically.
It'll look at the syntax.
It will parse that
into an abstract syntax tree,
which is a sort of
representation of functions
and classes
and statements
and variables
and operations
and all that kind of stuff.
your code,
your file,
your module,
basically,
becomes like a tree
and then what it does
is it then compiles that tree
by walking through
each of the branches
and walking through
and understanding
what the nodes are
and then there is a compilation.
Basically,
like, in the CPython compiler,
there's a function
for each type of thing
in Python.
so there's a compile binary operation
or there's a compile class function
and a compile class
will take a node
from the AST,
which has got your class in it
and it will then go through
and say,
okay,
what properties,
what methods does it have
and it will then go
and compile the methods
and then inside a method
it will go and compile the statements.
So, like,
once you break down
the compiler
into smaller pieces,
it's not that complicated
and what a compiler will do
is it will spit out
so compiled basic frame blocks
they're called
and then they get assembled
into bytecode.
So, after the compiler stage,
there is an assembler stage
which basically figures out
in which sequence
should the code be executed,
you know,
which basically,
like,
what will the control flow be
between the different parts of code,
the different frames.
In reality,
like,
they get executed
in different orders
because they depend on input
whether or not
you call this particular function
but still,
like,
if you've got a for loop,
then it's still got to go
inside the for loop
and then back to the top again.
Like,
that logic is,
like,
hard-coded into the for loop.
Right.
You know,
as you're talking,
I'm wondering if,
you know,
minor extensions
to the language
might let you
do higher-level optimizations.
Like,
say,
like,
having a frozen class
that you're saying
I'm not going to add any fields to
or,
like,
an inline on a function,
like,
I only,
or make it a
function internal
to a class
in which it could be
inlined,
potentially,
because,
you know,
no one's going to be able to,
like,
look at it from the outside
of this code and stuff.
What do you think?
There is an optimizer
in the compiler
called the peephole optimizer.
And when it's compiling,
I think it's actually
it's after the compilation stage,
I think,
it goes through
and it looks at the
code that's been compiled
and if it can make some
decisions about
either,
like,
dead code
that can be removed
or branches
which can be simplified,
then it can basically
optimize that.
And that will make
some improvement,
like,
it will optimize
your code slightly.
Right.
But then once it's done,
basically,
your Python application
has been compiled down
into this,
like,
assembly language
called bytecode,
which is the,
like,
the actual individual operations
and then they're
executed in sequence.
They're split up
into small pieces,
they're split up
into frames,
but they're executed
in sequence.
Right.
And if you look
at the C source code,
dive into there,
there's a C eval.c file
and it has,
like,
the world's largest
while loop
with a switch statement
in it,
right?
Yeah.
So this is,
like,
the kind of the brain
of CPython.
Oh,
maybe it's not the brain,
but it's the bit that,
like,
goes through each
of the operations
and says,
okay,
if it's this operation,
do this thing,
if it's that one,
do this thing.
This is all compiled in C,
so it's fairly fast,
but it will basically
sit and run the loop.
So when you actually
run your code,
it takes the assembled
bytecode
and then for each
bytecode operation,
it will then
do something.
So,
for example,
there's a bytecode
for add an item
to a list.
So it knows that
it will make a value
off the stack
and it will put that
into the list
or this one
which calls a function.
So,
if the bytecode
is call function,
then it knows to
figure out how to
call that function
in C.
Right.
Maybe it's loaded
a few things
on the stack,
it's going to call it,
do it just get sucked along,
something like that.
And so I guess
one of the interesting things,
and you were talking
about an interesting
analogy about this,
sort of when Python
can be slow
versus a little bit
less slow,
it's the overhead
of like going
through that loop,
figuring out what to do,
like preparing stuff
before you call
the CPython's
thing,
right?
Like list.sort,
it could be super fast
even for a huge list
because it's just going
to this underlying
C object
and say,
in C,
go do your sort.
But if you're doing
a bunch of
small steps,
like the overhead
of the next step
can be a lot higher.
in the nbody problem,
the step that it has
to do,
the operation it has
to do,
will be add number
A to number B,
which on a decent CPU,
I mean,
this is like nanoseconds
in terms of time
it takes to execute.
So if it's basically,
if the operation
that it's doing
is really tiny,
then after doing
that operation,
it's got to go
all the way back
up to the top
of the loop again,
look at the next
barcode operation,
and then go and run
this,
you know,
call this thing,
which runs the operation,
which takes again
like nanoseconds
to finish,
and then it goes
all the way back
around again.
So I guess the analogy
I was trying to think
of with the nbody problem
is,
you know,
if you were a plumber
and you got called out
to do a load of jobs
in a week,
but every single job
was,
can you change
this one washer
on a tap for me,
which takes you like
two minutes to finish,
but you get a hundred
of those jobs
in a day,
you're going to spend
most of your day
just driving around
and not actually doing
any plumbing.
You're going to be
driving from house
to house
and then doing
these like two
minute jobs
and then driving
on to the next job.
So I think the nbody
problem,
that's kind of an
example of that
is that the evaluation
loop can't make
decisions,
like it can't say,
oh,
if I'm going to do
the same operation
again and again
and again,
instead of going
around the loop
each time,
maybe I should just
call that operation
the number of times
that I need to.
and those are the
kind of optimizations
that a JIT would do
because it kind of
changes the compilation
order in sequence.
So that's,
I guess like we could
talk about there are
JITs available for
Python.
Yes.
CPython doesn't have,
CPython doesn't use a JIT,
but for things like the
nbody problem,
instead of the,
you know,
the plumber driving to
every house and doing
this two minute job,
why can't somebody
actually just go and,
why can't everyone
just send their tap
to like the factory
and he just sits in
the factory all day
replacing the washers.
Like Netflix of taps
or something,
yeah.
Back when they sent
out DVDs.
Maybe I was stretching
the analogy a bit,
but,
you know,
basically like you can
make optimizations
if you know you're
going to do the same
job again and again
and again,
or maybe like he just
brings all the
washers with him
instead of driving
back to the warehouse
each time.
So,
like there's
optimizations you can
make if you know
what's coming.
But because the
CPython application
was compiled ahead of
time,
it doesn't know
what's coming.
There are some opcodes
that are coupled
together,
but there's only a few
like which ones
they are off the top
of my head,
but there's only a
couple and it doesn't
really add a huge
performance increase.
Yeah,
there have been some
improvements around
like bound method
execution time and
methods without keyword
arguments or some
something along those
lines that got quite a
bit faster.
But that's still just
like how can we make
this operation faster?
Not how can we say
like,
you know what,
we don't need a
function,
let's inline that.
It's called in one
place once,
just inline it,
right?
Things like that.
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 having
difficulties or are
encountering errors
with your app right
now?
Would you even know
it until they send
that support email?
How much better would
it be to have the
error details immediately
sent to you,
including the call
stack and values of
local variables,
as well as the
active user stored in
the report?
With Sentry, this is
not only possible,
it's simple and free.
In fact, we use Sentry
on all the Talk Python
web properties.
We've actually fixed a
bug triggered by our
user and had the
upgrade ready to roll
out as we got the
support email.
That was a great email
to write back.
We saw your error and
have already rolled out
the fix.
Imagine their surprise.
Surprise and delight
your users today.
Create your free account
at talkpython.fm
slash Sentry and
track up to 5,000
errors a month across
multiple projects for
free.
So you did say there
were some.
There was Pigeon,
there's PyPy,
there's Unladen
Swallow, there's
some other options as
well, but those are the
JITs that are coming to
mind.
Piston, all of those
were attempts and I
have not heard anything
about any of them for a
year, so that's
probably not a super
sign for their
adoption.
Yeah, so the ones I
kind of picked on
because I think they've
got a lot of promise
and kind of show a big
performance improvement
is PyPy, which
shouldn't be new.
I mean, it's a popular
project, but PyPy uses
a...
PY, PY, because some
people say like Python
package inject, they
also call it PyPy, but
that's a totally different
thing.
Yeah, so PyPy...
Just for listeners who
aren't sure.
PyPy kind of helped
solve the argument for
my talk actually, because
if Python is slow, then
writing a Python
compiler in Python should
be like really, really
slow.
But actually, PyPy, which
is a Python compiler
written in Python, in
problems like the n-body
problem, where you're
doing the same thing
again and again, it's
actually really good at
it.
Like, it's significantly...
It's 700-something
percent faster than C
Python at doing the
same algorithm.
Like, if you copy and
paste the same code and
run it in PyPy versus
CPython, yeah, it will
run over seven times
faster in PyPy, and
PyPy is written in
Python.
So it's an alternative
Python interpreter that's
written purely in
Python.
But it has a JIT
compiler.
That's probably the big
difference.
Yeah.
As far as I understand it,
PyPy is kind of like a
half JIT compiler.
It's not like a full JIT
compiler like, say, C
Sharp or Java, in that it
will, like, run on C
Python and then, like,
decide to JIT compile the
stuff that's run a lot.
I feel like that's the
case.
PyPy is a pure JIT
compiler, and then
number is a, you can
basically choose to JIT
certain parts of your
code.
So with number, you can
use a, actually, a
decorator, and you can
stick it on.
An at JIT.
Yeah, it literally is
that.
You can do an at JIT on a
function, and it will JIT
compile that function for
you.
So if there's a piece of
your code which would
work better if it were
JITed, like it would be
faster, then you can just
stick a JIT decorator on
that using the number
package.
Yeah, that's really cool.
Do you have to, how do
you run it?
I've got some function
within a larger Python
program, and I put an at
JIT on it.
Like, how do I make it
actually JIT that and,
like, execute?
Can I still type Python
space, I think, or what
happens?
I don't know.
Do you know?
Yeah, I'm just wondering,
like, it probably is the
library that, as it pulls
in what it's going to give
you back, you know, the
wrapper, the decorator, the
function, it probably does
JIT.
So interesting.
I think that's a really
good option.
Of all the options,
honestly, I haven't done
anything with Numba, but
it looks like probably the
best option.
It sounds a little bit
similar to Cython, but
Cython's kind of the
upfront style, right?
Like, we're going to
pre-compile this Python
code to see, whereas
Numba, it sounds more, a
little more runtime.
Yeah, so Cython is not
really a JIT or a JIT
optimizer.
It's a way of decorating
your Python code with
type annotations and
using, like, a sort of
slightly different syntax
to say, oh, this
variable is this type,
and then Cython will
actually compile that
into a C extension
module, and then you run
it from CPython.
So it basically, like,
compiles your Python into
C and then loads it as a
C extension module, which
can make a massive
performance improvement.
Yeah, so you've got to
run a, like, a set of
py build command to
generate the libraries, the
.o files, or whatever the
platform generates, and
then those get loaded in.
Even if you change the
Python code that was their
source, you've got to
recompile them, or it's
just still the same old
compiled stuff, same old
binaries, yeah.
You can automate that so
you don't have to type it
by hand, but I think
Cython is a really good
solution for speeding it
up.
But as I kind of
pointed out in my talk,
it doesn't answer the
question of why Python is
slow.
It says, well, Python can
be faster if you do C
instead.
Yeah.
One thing I do like about
Cython these days is they've
adopted the type hints,
type annotation format.
So if you have, what is
that, Python 3, 4, or
later type annotations, you
got to be explicit on
everything.
But if you have those,
that's all you have to do
to turn it into like
official Cython, which is
nice because it used to be
you'd have to have like a
C type or Cython type dot
int rather than a, you
know, colon int or
something funky like
that.
Yeah.
And it's nice that they
brought the two things
together.
Cython like had type
annotations before the
language did, I think.
Right.
Yeah.
So they had their own
special way.
They had their own special
little sub language that
was Python-esque, but not
quite.
So I was looking at this
nbody problem and I
thought, all right, well, I
probably should have played
with Numba, but I have a
little more experience with
Cython.
So let me just see, like
the code is not that hard
and I'm going in terms of
like how much code is there
or whatever.
Sure.
The math is hard, but the
actual execution of it
isn't.
So I'll link to the actual
Python source code for the
nbody problem.
And I ran it.
It has some defaults that
are much smaller than the
one you're talking about.
So if you run it, just
hit run.
It'll run for like two on
my machine.
It ran for 213
milliseconds just in pure
CPython.
So I said, all right, well,
what if I just grab that
code and I just plunk it
into a PYC file unchanged.
I didn't change anything.
I just moved it over.
I got it to go into 90
milliseconds, which is like
2.34 times faster.
And then I did the type
hints that I told you
about.
Because if you don't put
the type hints, it'll
still run, but it will
work at the, the, the
pie object level.
Like, so your numbers are
pie object numbers, not,
you know, ints and
floats down.
So you make it a little
bit faster.
So, but I was only able
to get it four times
faster down to 50
milliseconds.
Either I was doing it
wrong or that's just about
as that much faster as I
can get it.
I could have been missing
some types and it was
still doing a little more
CPython interrupt stuff.
But yeah, I don't know.
It's, it's an interesting
challenge.
I guess the last thing to
talk about, like on this
little bit right here is
the, is my PYC.
Yeah.
I didn't know much about
my PYC.
I don't know a lot
about it either.
So my PY is a type
checking library and
verification library for
the type annotations.
Right.
So if you put these type
annotations in there, they
don't do anything at runtime.
They're just like there to
tell you stuff.
Right.
But things like certain
editors can partially check
them or my PY can like
follow the entire chain and
say this code looks like
it's typewise hanging
together.
Not like a pure five
levels.
pass an integer and you
expect a string.
So it's broken.
Right.
It can check that.
So they added this thing
called my PYC, which can
take stuff that is
annotated in a way that
my PY works with, which
is basically type
annotations, but more.
And they can compile that
to see as well, which
they also interestingly got
like a four times speed
up with stuff, not in
the embody problem, but
on my PY.
So I don't know.
It's, there's a lot of
options, but as you point
out, they are a little
bit dodging Python.
The number stuff is cool
because I think you
don't really write
different code.
Do you?
Yeah, it's been more
natural.
And I think PYPY, like
you're saying you kind of
got two to four times
improvement by moving
things to Siphon.
And it took a decent
amount of work, right?
Because every loop
variable had to be
declared somewhere else
because you can't set the
type or the type
annotation inside the
loop declaration, right?
Like it wasn't just
put a colon in.
I had to do like a
decent amount of work to
drag out the types.
Yeah.
And whereas PYPY will
be a seven times
improvement in speed,
for that problem.
Yeah.
And there's no C compilation.
Yes.
That's really nice.
So we talked about JITs
and JITs are pretty
interesting.
To me, I feel like
JITs often go together
with garbage collection
in the entirely
unmanaged sort of
non-deterministic sense
of garbage collection,
right?
Not reference counting,
but sort of the
mark and sweep style.
So Python,
I mean,
maybe we could talk
about GC
at Python first
and then
if there's any way
to like change that
or advantages there,
disadvantages.
From the
Instagram story
that they saw
a performance improvement
when they turned off GC.
Yeah, like
we're going to solve
the memory problem
by just letting it leak.
Like literally,
we're going to disable
garbage collection.
Yeah,
I think they got like
a 12% improvement
or something.
It was significant.
They turned it off
and then they just
restarted the worker processes
every 12 hours
or something like that.
And it wasn't that bad.
The GC itself,
like to your,
I said there's another
problem that I studied
which was the
binary tree problem.
And this particular
problem will show you
the impact of the
garbage collector
performance on,
like in this particular
algorithm,
this benchmark,
it will show you
how much your GC
slows down the program.
And again,
I wanted to compare
Node with Python
because they both have
both reference
counting and
garbage collection.
So the garbage
collector with Node
is a bit different
in terms of its design,
but both of them
are a stop everything
garbage collector.
So,
you know,
CPython has a
main thread,
basically,
and the garbage
collector will run
on the main thread
and it will run
every number of
operations.
So,
I think the,
I can't remember what
the default is,
it's like 3,000 or
something.
Every 3,000
operations in the
first generation
where an object
has been assigned
or deassigned,
then it will run
the garbage collector,
which goes and
inspects every,
every list,
every dictionary,
every,
what other types,
like custom objects,
and sees if they have
any circular references.
Right,
and the reason we need
the GC,
which does this,
is because it's not
even the main
memory management system,
because if it was,
Instagram would not
at all be able
to get away
with that trick.
Right,
this is like a,
a final net
to catch the stuff
that reference
counting doesn't
work.
Normally,
like if there's
some references
to an object,
once things stop
pointing at it,
the last one that
goes,
it just poof,
it disappears.
But the challenge
of reference
counting garbage
collection is if
you've got like
some kind of
relationship where
one thing points
at the other,
but that thing
also points back
to itself,
right,
like a couple
object,
right,
a person object
with a spouse
pointer or something
like that,
right?
If you're married,
you're going to leak.
Yeah,
absolutely.
So this is the thing
you're talking about,
those types of things
that's addressing.
And it's kind of
designed on the assumption
that most objects
in CPython
have very short
lifespans.
So,
you know,
they get created
and then they get
destroyed shortly
afterwards.
So like local
variables inside
functions or,
you know,
like local variables
inside list
comprehensions,
for example,
like those can be
destroyed pretty much
straight away.
But the garbage
collective will stop
everything running
on the main thread
while it's running
because it has to
because you can't,
you know,
if it's deleting stuff
and there's something
else running at the
same time that's
expecting that thing
to exist,
it's going to cause
all sorts of problems.
So yeah,
the GC will kind of
slow down your
application if it gets
hit a lot.
And the binary tree
problem will basically
construct a series of
trees and then loop
through them and then
delete the nodes and
the branches,
which kind of triggers
the GC to run a lot.
And then you can
compare the performance
of the garbage
collectors.
So one thing I kind of
noted in the design is
that they stop
everything.
If the time it takes to
run the garbage
collector could be as
short as possible,
then the performance
hit of running it is
going to be smaller.
And something that Node
does is it runs a
multi-threaded mark
process.
So when it actually goes
and looks for circular
references,
it actually starts
looking before it
stops the main thread
on different helper
threads.
So it starts separate
threads and starts the
mark process.
And then it still stops
everything on the main
process, but it's kind
of prepared all its
homework ahead of time.
It's already figured
out what is garbage
before it stops stuff.
And it's like, now we
just have to stop what
we throw it away and
update the pointers and
then you can carry on,
right?
Because it's got to,
you know, balance the
memory and stop
allocation and whatnot.
Yeah.
So I think technically
that's possible in CPython.
I don't think it has
anything to do with
the GIL either, like
why that couldn't be
done.
You could still do...
Right.
It seems like it
totally could be done.
Yeah.
Because marking and
finding circular
references could be done
outside of the gill
because it's a C-level
call.
It's not an opcode.
But like I say in the
talk, you know, all this
stuff that I've listed
so far is a lot of
work and it's a lot of
engineering work that
needs to go into it.
And if you actually look
at the CPython compiler,
like the CEval, and look
at the number of
people who've worked
on or contributed to
it, it's less than 10
like to the core
component.
I wouldn't want to
touch it.
I would not want to
get in there and be
responsible for that
part of it.
No way.
Yeah.
And at this stage,
they're minor
optimizations.
They're not sort of
big overhauls because
there just isn't the
people to do it.
Yeah.
You made a point in
your PyCon talk that,
you know, the reason
that V8 got to be so
optimized so fast is
because it's got, you
know, tens of millions
of dollars of
engineering put against
it yearly.
Right?
I mean, it's kind of
part of the browser
wars.
The new browser wars
a bit.
Yeah.
From what I could
work out, there's at
least 35 permanent
developers working on
it.
Just looking at the
GitHub project, like if
you just see the
commit histories, like
nine to five, Monday
to Friday, 35 advanced
C++ developers hacking
away at it.
Right.
If we had that many
people continuously
working on CPython's
like internals and
garbage collection and
stuff, we'd have more
optimizations or bigger
projects that people
will try to take on
probably.
Yeah, absolutely.
And the people who
work on it at the
moment, all of them
have day jobs and this
is not typically their
day job.
Like they managed,
they've convinced their
employer to let them do
it in their spare time
or, you know, one or
two days a week, for
example, and they're
finding the time to do
it.
And it's a community
run project.
it's an open source
project.
But I think kind of
going back to places
where Python could be
faster, like these kind
of optimizations in
terms of engineering,
they're expensive
optimizations.
They cost a lot of
money because they need
a lot of engineering
expertise and a lot of
engineering time.
And I think as a
project at the moment,
we don't really have
that luxury.
So it's not really fair
of me to complain about
it if I'm not
contributing to the
solution.
Yeah, but you have a
day job as well, right?
But I have a day job and
this is not day job.
So yeah, I think there's,
I think for what we use
Python for most of the
time, it's definitely
fast enough.
And in places where it
could have optimizations
like the ones that we
talked about, those
optimizations have
drawbacks because, you
know, adding a JIT,
for example, means that it
uses a lot more
memory.
like the Node.js
example, the n-body
problem, sure, it
finishes it faster, but
uses about five times
more RAM to do it.
Right.
And PyPy uses more
memory, like the JIT
compiler, and also the
startup time of the
process is typically a
lot longer.
If anyone's ever tried
to boot Java JVM
cold, you know, like
the startup time for
JVM is pretty slow.
.NET's the same, like
the initial boot time
for it to actually get
started and warm up
is time consuming.
So you wouldn't use
it as a, like a
command line tool to
write a simple script
that you'd expect to
finish in, you know,
under 100 milliseconds.
I think that that kind
of highlights one of the
challenges, right?
It's if you thought your
process was just going to
start and be a web
server or a desktop
application, two
seconds start of time
is fine, or whatever
that number is.
But if it's solving this
general problem, yeah, it
could be running Flask as
a microservice, or it
could be, you know,
replacing Bash, right?
Like these are very
different constraints and
interests, right?
Yeah.
And there aren't really
many other languages where
there is one sort of
language definition and
there are multiple
mature implementations of
it.
So, you know, with
Python, you know, you've
got Cython, you've got
PyPy, you've got
Numba, you've got
LionPython.
I mean, there's like a
whole list of, you know,
different, Jython, like
different implementations
of the language.
And people can choose
the, I guess, kind of
pick which one is best for
the problem that they're
trying to solve, but use
the same language across
them.
Whereas you don't really
have that luxury with
others.
You know, if you're
writing Java, then you're
using JVM.
There are, I mean, there's
two implementations.
It's the free one and
the licensed one, but
like that's pretty much
as far as it goes.
That's not exactly the
same trade-off.
Yeah.
It's optimizing for
money.
That's not optimizing for
performers or whatever
necessarily.
So one thing that I feel
like comes around and
around again in this
discussion, and I'm
thinking mostly of like
PyPy and some of these
other attempts people
have made to add like
JIT compilation to the
language or other
changes.
It's always come back,
it seems like, to
well, it would be
great to have these
features.
Oh yeah, but there's
this thing called the
C API.
And so no, we can't
change the GIL.
No, we can't change
memory allocation.
No, we can't change any
of these other things
because of the C API.
And so we're stuck.
Yeah.
I mean, I'm not saying
I'm asking you for a
solution here.
like, I just, it feels
like that is both the
real value of Python in
that like some of the
reasons that we can still
do insanely computational
stuff with Python is
because a lot of these
libraries where they have
these tight loops or
these little bits of code
deserialization or matrix
multiplication or whatever,
they've written that in C
and then ship that as a
wheel.
And so now all of a sudden
our code is not slow as
doing math with Python,
as fast as doing math
with C.
Yeah.
I mean, so if you look
at a NumPy, for example,
if you're doing a lot of
math, then you, you know,
you could be using the
NumPy library, which is
largely compiled C code.
It's not like you import
it from Python and you run
it from Python, but the
actual implementation is a
C extension.
And that wouldn't be
possible if CPython wasn't
built in the way it is,
which is that it is a
ahead of time extension
loader that you can run
from Python code.
Yeah.
One project I do want to
give a shout out to, I
don't know if it's going to
go anywhere.
It's got a decent amount of
work on it, but it's only
got 185 GitHub stars.
So take that for what it's
worth.
This thing called HPY, H-P-Y.
Guido Van Rossum called this
out on Python Bytes 179 when
he was a guest co-host there.
And it's an attempt to make a
new replacement of the C API
for Python, where instead of
pass around pointers to
objects, you pass basically
pointers to pointers, which
means that things that move
stuff around like compacting
garbage collectors or other
implementations like JITs have a
much better chance to change
things without directly breaking
the C API.
You can change the value of the
pointer pointer without, you
know, having to reassign that
down at that layer.
So they specifically call out
it's, you know, the current C API
makes it hard for things like
PyPy and Grail Python and JITon.
And the goals are to make it
easier to experiment with these
ideas, more friendly for other
implementations, reference
counting, for example, and so on.
So anyway, I don't know that's
going anywhere, how much traction
it has, but it's interesting
idea.
Yeah, no, I like the idea.
And the C API, like, has come a
long way, but it's got its
quirks.
I don't know, there's been a lot
of discussions, and there's a lot
of draft peps as well, you know,
proposing kind of different
designs to the C API.
Yeah.
So we're getting kind of short on
time.
We've discussed a bunch of stuff.
I guess two other things I'd like
to cover real quickly.
One, we've talked about a lot of
stuff in terms of computational
things, but understanding memory
is also pretty important.
And we did just talk about the GC.
It's pretty easy in Python to just
run C profile and ask what my
computational time is.
It's less obvious how to
understand memory allocation and
stuff.
And was it you that recommended
Austin to me?
Yeah.
Yeah, so Austin is a super cool
profiler, but does both CPU
profiling, but also memory
allocation profiling and tracing
in Python.
Do you want to tell people about
Austin real quick?
Yeah, so Austin is a new
profiler written for Python code.
It's a sampling profiler, so it
won't, like other profilers, it
won't slow your code down
significantly.
It's kind of basically sits on
the side, just asking your app,
you know, what it's doing as a
sample.
And then it will give you a whole
bunch of visuals to let you see,
like flame graphs, for example,
like what's being called, what's
taking a long time, which functions
are chewing up your CPU, like
which ones are causing the
bottlenecks and then which ones are
consuming a lot of memory.
So if you've got a, you know, a
piece of code that is, it is slow,
the first thing you should probably
do is just stick it through a
profiler and see if there is a
reason why, like if there is
something that you could either
optimize or, you know, you've
accidentally done like a nested
loop or something and Austin
would help you do that.
One of the things I thought was
super cool about this, like the
challenge I have so often with
profilers is the startup of
whatever I'm trying to do, it just
overwhelms like the little thing I'm
trying to test.
you know, I'm like starting up a web
app and initializing database
connections and I just want to
request a little bit of some
paid and it's not that slow, but
you know, it's just, I'm seeing all
this other stuff around and I'm just
like, I just want to focus on this
one part of it and they've got all
these different user interfaces, like
a web user interface in a terminal
user interface.
They call it two, which is cool.
And it gives you like a, like kind
of like top or glances or one of
these things that tells you right now,
here's what the profile for the
last five seconds looks like.
And it gives you the call stack and
breakdown of your code right now for
like that five second segment, like
updating in real time.
That's super cool.
Yeah.
So if you want to run something and
then just see what it's doing or you
want to replay it.
Why is it using a lot of CPU now?
Yeah.
That's, I really like that.
That's super cool.
All right.
Also, you know, concurrency is
something that Python has gotten a
bad rap for in terms of slowness.
I think with async and await and
asyncio, if you're waiting on an
external thing, Python can be ultra
fast now, right?
Like it's acing and awaiting waiting
on like database calls, web calls
with the right drivers, super fast.
But when it comes down to the
computational stuff, there's still
the GIL and there's really not a
great fix for that.
I mean, there's multiprocessing,
but that's got a lot of overhead.
So it's got to make sense, right?
Kind of like your plumber analogy,
right?
You can't do like one line function
calls in multiprocessing or, you
know, like one line computations.
But the work that Eric Snow's doing
with subinterpreters looks pretty
promising to unlock another layer.
Yeah.
So it's out in the 3.9 alpha.
If you've played with that yet, it's
still experimental.
So subinterpreters is somewhere in
between multiprocessing and
threading in terms of the like the
implementation.
So it will it doesn't spawn.
So if you use multiprocessing, I
mean, that's basically just saying
let's hire another plumber and we'll get
them to talk to each other at the beginning
of the day and split up the tasks.
Whereas subinterpreters, actually,
maybe they're sharing the same van.
I'm not sure where this analogy is going,
but, you know, they use the same process.
The subinterpreters share the same Python
process.
It doesn't spawn up an entirely new process.
It doesn't have to load all the modules
again.
And the subinterpreters can also talk to
each other.
they can use shared memory to communicate
with each other as well.
But because they're separate
interpreters, then technically they can
have their own locks.
So the lock that, you know, gets locked
whenever you run any opcode is the
interpreter lock.
And this basically means that you can
have two interpreters running in a
single process, each with its own lock.
So it can be running different
operations at the same time.
Right.
They would automatically run on separate
threads.
So you're basically running multi-threading
and it can also use multi-CPU.
That'd be great.
Fundamentally, the GIL is not about a
threading thing per se.
It's about serializing memory access
allocation and deallocation.
And so with the subinterpreters, the
idea is you don't directly share pointers
between subinterpreters.
There's like a channel type of
communication between them.
So you don't have to take a lock on
one when it's working with objects
versus another, like they're entirely
different set of objects.
They're still in the same process space,
but they're not actually sharing
pointers.
So you don't need to protect each
other.
Right.
You just have to protect within each
subinterpreter, which has a
possibility to let me use all six of
my cores.
Yeah, absolutely.
You can't read and write from the same
local variables for that reason,
which you can do in threading.
But with subinterpreters, it's kind of
like a halfway halfway between just
running a separate process.
Yeah.
It probably formalizes some of the
multi-threading communication styles
that are going to keep things safer
anyway.
Definitely.
Yeah.
All right.
Let's talk about one really quick
thing before we wrap it up.
Just one interesting project that
you've been working on.
I mentioned that you were on before
about some security issues, right?
Yeah.
I want to tell people about your PyCharm
extension that you've been working on.
Yeah.
So I've been working on a PyCharm
extension called Python Security.
It's very creatively named.
It's available.
Take the straightforward.
Yeah, exactly.
So it's basically like a code checker,
but it runs inside PyCharm and it will
look for security vulnerabilities that
you may have written in your code and
underline them for you and in some
cases fix them for you as well.
So it will say the thing you've done
here is really bad because it can cause
someone to be able to hack into your
code and you can just press the quick
fix button and it could fix it for you.
So it's got actually over a hundred
different inspections now.
And also you can run it across...
Should I use the YAML.load still?
Is that good?
No.
I think that was like the first checker,
right?
Actually, it was the YAML.load.
Yeah, you can run it across the whole project.
So you can do a code inspection across your project to like a code audit.
And also it uses PyCharm's package manager.
So it will go in and look at all the packages you have installed in your project and it will
check them against either Snyk, which is a big database of vulnerable Python packages.
Snyk.io uses their API.
So it checks it against that or you can check it against like your own list.
And also it's available as a GitHub action.
So manage to figure out how to run PyCharm inside Docker so that you can run PyCharm from
GitHub action.
Wow.
Yeah, you can write a CICD script in GitHub to just say inspect my code and it will just
inside GitHub.
You don't need PyCharm to do it, but it will run the inspection tool against your code repository.
It just requires that it's open source to be able to do that.
Okay, that's super cool.
All right, well, we're definitely out of time, so I have to leave it there.
Two quick questions.
Favorite editor, notable package?
What do you got?
PyCharm and I don't know about the notable package.
I don't know.
Yeah, you've been too far in the C code.
Yeah, I know.
I'm like, what are packages?
I think there's something that does install those, but they don't work down in C.
Yeah, no, that's cool.
All right, so people are interested in this.
They want to maybe understand how CPython works better or how that works and where and why
it might be slow so they can avoid that.
Or maybe they even want to contribute.
What do you say?
Wait for my book to come out and read the book or read the real Python article, which is free
and online.
And it talks through a lot of these concepts.
Yeah, right on.
Well, Anthony, thanks for being back on the show.
Great, as always, to dig into the internals.
Thanks, Michael.
Yeah, you bet.
Bye.
This has been another episode of Talk Python to Me.
Our guest on this episode was Anthony Shaw, and it's been brought to you by Brilliant.org
and Sentry.
Brilliant.org encourages you to level up your analytical skills and knowledge.
Visit talkpython.fm/brilliant and get Brilliant Premium to learn something new every
day.
Take some stress out of your life.
Get notified immediately about errors in your web applications with Sentry.
Just visit talkpython.fm/sentry and get started for free.
Want to level up your Python?
If you're just getting started, try my Python Jumpstart by Building 10 Apps course.
Or if you're looking for something more advanced, check out our new Async course that digs into
all the different types of async programming you can do in Python.
And of course, if you're interested in more than one of these, be sure to check out our
Everything Bundle.
It's like a subscription that never expires.
Be sure to subscribe to the show.
Open your favorite podcatcher 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.
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.