 So, first, a few words about me. I have been PyPyCore developers for about six years now. And well, it's basically my job to work on open source or to do consulting related to it. Well, I have mail on Twitter, original. So this talk, so before diving into C extensions and how we support them in PyPy, which is what's called CpyX, just a quick introduction to PyPy and a few words about what we are doing at the moment, especially because this is, I think, the only PyPy talk at the conference. So PyPy, that's our elevator pitch, PyPy is a fast, compliant alternative implementation of the Python language. So what it means, well, first it means that we run Python programs. But more than that, we try to be compliant with, well, the specification of the Python language, which, as you probably know, is just the source code of Cpython. So that causes a few issues. But nevertheless, we manage to do that and to provide very decent speed for Python programs. So to have an idea of the architecture of PyPy, I like to start from the architecture of Cpython, which is a bit simpler. So, well, Cpython is written in C as you know. And in the Python executable, you have two main parts. You have the compiler that takes the source code, turns it into byte code. And you have the byte code interpreter that takes each byte code and runs a specific operation corresponding to the byte code. PyPy, it's quite similar, except that, well, in the interpreter, on top of the byte code interpreter, we have just in time compiler that works by observing hot loops in the code or any sort of code that runs often. And then this code is traced, analyzed, and so that it gets runtime information on the types. And with this, it emits optimized machine code. You know, the magic in PyPy comes mostly from the Python toolchain up there, which generates more or less automatically the desktop compiler and also adds other features that work with the JIT, like the GC. And the other good thing about this toolchain is that the language we use is our Python, which is a subset of Python, so we can easily test the interpreter. Now, another difference between Cpython and PyPy, it's to do with the memory management. So in Cpython, it's just a standard C application, so it uses just malloc and free and all that, which is not ideal for an interpreter that tends to allocate small objects in random places all the time. And also, due to this simplistic model, Cpython needs to update reference counts all the time. And in contrast, PyPy has quite a performance garbage collector, which is specially tuned for Python. And that way, and also it works well with the JIT. And well, and there's an observable user difference, which is when at which time does the location happen? In PyPy, since we have a garbage collector, it's the allocation happens when the object is collected by the garbage collector, whereas in Cpython, it's when the reference counts hit zero, which happens because you've told the interpreter to decrement the reference count. In Cpython, you know when the allocation happens, usually. And another, well, the most interesting difference between PyPy and Cpython, it's speed. So people often say that PyPy provides magic speedups. And it often does. But it can do more than that. It's true that, well, if you're lucky, you can have your program that you use to run on Cpython, try PyPy, and cool, it's twice faster. That sounds great. But actually, if you, in such a case, if you try to optimize for PyPy, you could get your code to be four times faster than on Cpython. So why stop at just running PyPy if you want to read the benefits, optimize. And, well, as usual, if you want to optimize, you first need to have good benchmarks. And so a good benchmark is something you really care about. So it's not a microbenchmark. It's not a test. It's something that matches the way you really use your code. And once you have a benchmark, well, you should use a profiler to find the whole part of the code. And on PyPy, there's basically only one profiler that understands PyPy. It's called VMProf. And once you've done that, then you can actually modify the code and a few pointers for doing that. First, you need to avoid dynamic features at run times, or at least in a loops. So it's fine to use meta programming as much as you want. Before you run your main computation, but in the inner loop, there should be none of that. But what a side benefit of having a JIT is that you can use function calls because function calls are usually in line by the JIT. So you're free to structure your code. And there are a few optimizations that you need to be aware of in order to hit them and or to avoid disabling them by accident. So the one is that attribute access on instances is well optimized. Effectively, it's as if every class had a slots declaration and you don't need to look up an attribute name in a dict. And the other thing is that we have what we call list strategies. So when you have a list that contains a homogeneous list containing built-in types like ints or floats, then access will be very fast. It's as if you're indexing a C array. So let's have a few words about the current status. I'll start by Python 3. So we had our first free-supported release of, well, of any PyPy3 last Christmas. The latest version was released in April. So what we support is 3.5 at the moment. And it's, well, just there are still things missing on Windows, but it's fully supported on Linux, Mac, BSD. And so what we're working on, it's 3.6. We are about halfway, which is always a hard thing to say. But I hope there's not that much left. At least the main syntax features are there. Now it's only the details. And for Python 3, we need to work on more optimizations for things that, well, for things that didn't exist in Python 2. The fact that we have Unicode everywhere, async stuff is, we need to find ways to make it a bit better. It's decent, but it could be better. And well, that's it for Python 3. Python 2 had a release at the same time as Python 3. And so in this release, apart from the, well, yeah, in this release, we mainly had a few incremental improvements. So I remind you that CFFI is still the best way to talk to C code. And it keeps being updated with every release of PyPy. But the main highlight of the last release was the improvements to the extension compatibility and speed. And so now you can PyPy install the main scientific libraries. And we have a few wheels that you can check because they are not, you have no PyPy wheels on PyPI because of issues with many Linux. So if you use PyPy, check out this website and you'll get binary wheels. So with this, I start the main part of the talk, which is CPyX. So CPyX, well, what is CPyX really? As a user, you could just think of it as Python.h for PyPy. When you need to compile a C extension, you link it against Python.h, well, you do that on CPy and on PyPy, you do exactly the same. And with this Python.h, the compiler will know where to find the actual C function you want to call from the extension. So what's in CPyX, well, this Python.h is mostly generated during the PyPy build. And we have a bit of C code, sort of 100 in C code, which is mostly a copy from CPy. But most of the implementation comes from our Python implementation. And well, what does it look like? Looks like this. So this is our Python code. This is the code that implements PyList setItem. And if you've already looked at the CPyX source, you'll notice that it's mostly the same logic. Well, we have some error checking. And then we find where the, well, we fetch the place where we store some item in the list and we update. And we do a decref because the old object is not referenced anymore. So this is, well, this looks quite simple. And this is, the good thing is that it is debuggable and testable because it is using our Python. However, this hides a lot of complexity which is in this decorator at the top. And this decorator at the top does all the magic of moving between the C world and the Python world. And there's quite a lot of things to do like all that and that. And so what do we need to do to make CPy's work? So we have two very different worlds. And we have a C extension that tries to talk to the PyPy interpreter. So the implementation languages are different. But this means that the internal object structures are different. We have a completely different memory model inside PyPy. We have managed memory. We have garbage collector that can move the objects around. But that's quite incompatible with the C model where you refer to things using pointers. And pointers are basically locations in memory. So if things move around, C code gets confused. And PyPy, we have also a lot more control on what's going on. We have exceptions that can propagate. And so we have internal exception inside the interpreter that used, for instance, to implement the user level exceptions. And but inside the C extension, there are no exceptions in C. So extensions need to set the thing called an error indicator that the interpreter needs to check from time to time. Also C extensions need to deal with ref counts because you need to know when an object can be deallocated. But inside PyPy, we have garbage collector, so we don't worry about that at all. And so how do we bridge this? And the main idea is to have these C objects linked to Python objects. So they thought that they are basically just proxies for the actual object. And the specific mechanism is just this line here. So if you know Cpython, you'll notice that this PyPy length is the only difference with what Cpython does. And this PyPy link allows the Cpyx layer to query the memory manager in order to find the PyPy object. And well, and then it's just a matter of, well, it's a small matter of programming to implement all the C semantics. So you need to remember your ink graphs and dec graphs when you are implementing API functions in Python. But there are more problems like what do you do when, if you want to call a C function from Python, which is the whole point of having a C extension. So that's why we have these magic decorators that handle the transformation magically. And basically, yeah, we had levels of indirection. Any time we need to cross a boundary, any time there's something to check, because we are back from the C world where anything can happen. So that's the general idea. But, well, you probably know the rest of this quote, or actually folks saying, which is that all problems in computer science can be solved by another level of indirection except the problem of too many layers of indirection. And we've recently noticed that we had too many layers of indirection. So this was last year in Cape Town. And we've started to improve the performance of CPYX. So as I said, crossing this boundary is expensive. And because we have this magic going on, we do it even without realizing. Because we don't need to explicitly convert between the C world and the PyPy world. It happens behind the scenes. So we need to be more careful about that. And by being more careful, we can remove a lot of this back and forth. Because it's, in many cases, it turned out to be completely useless. And well, it's an extension of this idea, which is when you have an operation that can happen completely on the seaside, then we should just implement it in C and never cross the boundary. So well, this requires some care as well because of the different underlying assumptions of PyPy and of the C world. But it's possible. And while we've started doing it, it's not complete. But the results are here. So in blue, it's just a C Python as a reference. Green is PyPy before these optimizations. And red is after. And this is a bunch of rather silly microbenchmarks. So that we just check the overhead of each specific operation. So we used to be between four and six times slower than C Python. And now we are between the same speed and 50% faster. So there's hope for the speed of C PyAxt. And the future, while it's to continue working on this, we can talk. There were a few red bars where we were still a lot slower than C Python. It's probably doable to be faster than C Python in all cases. It just requires work. And well, we have PyPy OpenSpace this afternoon at 2. So if you want to come discuss with your own PyPy problems or your ideas, well, that's it. I'm waiting for your questions. Anyone questions? Were there any C extensions that this PyAxt was written in mind for, or is it just for the general case? And does that make sense? No, it's for the general case. Well, there are extensions we've been using as tests. And also because there are extensions we would really like to make compatible with PyPy. And well, it was mainly NumPy. And it's now fully supports PyPy now, I think. No, because I don't know. Sorry? Harry Swan. Eh? Harry Swan. Harry Swan. When will PyPy drop support for Python 2? The current official answer is never. Any reasons for that? Well, a lot of our users are still on Python 2. And our tool chain is implemented in Python 2. So that's not a motivation. And also as language implementers, we are quite happy to have something that doesn't change. And so, well, these are all the reasons why PyPy likes Python 2. And well, we see no real reason to drop Python 2. We have two branches for Python 2 and Python 3 that share a lot of code. So it's not a major burden to keep supporting Python 2 either. I have a question about AirPython. Yes. How is it related to Cpython? Is it a subset or something like that? Well, it is a subset. So you can run our Python code as Python code. And that's what we do a lot for testing. And that's quite convenient. But the exact definition of our Python is just the subset of Python that can be understood by the tool chain. So what about Cytan? Does this extension, the compatibility layer, make PyPy compatible with Cytan? Yes, yes. Cytan is fully, well, I won't say fully because there's one piece of code in Cytan that's Cpython specific. But apart from that, Cytan is fully supported. And extensions that use Cytan now work on PyPy. Scikit-learn, which uses a lot of Cytan, has experimental support for PyPy, for instance. And LXML has supported PyPy for years. Thank you very much.