Hello, and welcome to C++ Weekly. I'm your host, Jason Turner, available for code reviews and
on-site training. In this episode I am going to follow up on a promise. I promised you that we
were going to talk about this Codon version, or Codon, Codon version of this game of life
Simulator for Python and how it compares to the C++ version, but I did say that I
had to record an additional episode before I could get there. So hopefully you have watched
the episode on modulus, because that becomes very important in this episode of C++ Weekly.
[Music] For review, I have a bunch of different versions of a Game of Life simulator written
in C++ and Python and Python mixed with C++, and we tested them against Python with a jit,
regular Python, all the things. Okay. I'm going to do a quick recompile of the C++ version. We have
our fixed up version of the regular life.py, which moved the neighbors out of the count_neighbors
function and moved it into the Automata class so that it is not reconstructed every single time we
go to count the neighbors, and then that brings us to Codon. We should mention what Codon is - by
their own definition, "Codon is a high performance Python compiler that compiles Python code to
native machine code without any runtime overhead. Typical speed ups over Python are on the order of
10 - 100 times or more, on a single thread." It is "typically on par with, and sometimes better than,
that of C and C++. Unlike Python, Codon supports native multithreading, which can lead to speed
ups." Now I'm going to say I'm barely scratching the surface of this project because it supports
all kinds of things such as magic built-in GPU kernels, and that would be absolutely amazing,
but we're not going to touch on that in this episode. We just need to look at the version
of our Game of Life simulator that works with Codon. It took some porting. It is not directly
Python. We have to give types, we have to tell it that this Point class has an x and a y, fine,
that are both integers. For some functions we don't have to, but for classes we do. It's a
lot like C++ in this regard. It has to know what kind of class it's creating, how big it is and
what types it contains, but otherwise the logic stays exactly the same. So let's run a quick test
here. First of all, Codon works in jit and fully compiled modes. Okay, so I have this
downloaded into a .directory codon/bin/codon, and I'm going to compile my version of it. Now
this is a version that was just downloaded in February of 2023 from their official binaries,
and there are two different modes at least, three different modes. We can build, run
and generate documentation. We want to build, but this is by default building
a debug build --release. We want to enable compiler optimizations.
Okay, and that should have built, yes, this life-codon. So let's go ahead and
double check that we are doing the exact same number of iterations for our life.py.
We were doing 10,000 iterations, and for life.cpp
10,000 iterations, so let's get the Codon one up to 10,000, recompile it and re-execute it.
Okay we're looking at in the ballpark of 8 seconds, and if you remember, the
C++ version is 0.09 seconds, which is quite a bit faster; and we're going to go ahead and also time
the PyPy jitted version just to make sure that we're comparing all the same things.
So at the moment, the jitted version is twice as fast as the compiled version,
which doesn't really make a whole lot of sense to me, but we do have that mistake still hanging out
in this code where we have the lookup for the set of neighbors being defined inside of the function.
So let's move that out and see if the compiled version can get a little bit smarter. I don't
know if I'm gonna have to give it a type since I'm declaring it inside the class here or not.
Okay, so we were able to cut the run time in half by moving that out. A little bit better
than half actually, so 3.5 seconds. Still nowhere near the C++ version,
but finally a little bit faster than the jitted version. Interesting, but I did promise you that
modulus would come up in this, and let's go ahead and take a quick look at that.
You might have noted that I have this right here as I was scrolling through the code - modulo.
Codon does not use floor modulus like Python does, so if you are porting your algorithm
from Python to Codon, which you might think of as Python, you have to implement your own
modulus operator if you want to keep the same behavior for your wraparound index lookups.
That was quite the adventure in my attempts to port this code over to compiled Python, so I just
saved you all a bunch of time if you were looking at that. Just keep your eye open for that tricky
modulus operator as you're moving algorithms from one programming language to another because it's
entirely possible that's going to bite you. So thanks for watching this episode of C++ Weekly.
[Music]