I have a C++ class with some methods that use std::thread that I'm making accessible to Python via Cython. Do you know where in my Cython code I'd want to put the nogill directive? Would I want to put it when I declare the class methods or when I create a Cython wrapper class? I've used the example class from the Cython docs below:
Declaring the class:
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Rectangle:
Rectangle() except +
Rectangle(int, int, int, int) except +
int x0, y0, x1, y1
int getArea()
void getSize(int* width, int* height)
void move(int, int)
Cython wrapper class:
cdef class PyRectangle:
cdef Rectangle c_rect # hold a C++ instance which we're wrapping
def __cinit__(self, int x0, int y0, int x1, int y1):
self.c_rect = Rectangle(x0, y0, x1, y1)
def get_area(self):
return self.c_rect.getArea()
def get_size(self):
cdef int width, height
self.c_rect.getSize(&width, &height)
return width, height
def move(self, dx, dy):
self.c_rect.move(dx, dy)
Cython allows you to release the GIL. That means that you can do multi-threading in at least 2 ways: Directly in Cython, using OpenMP with prange. Using e.g. joblib with a multi-threading backend (the parts of your code that will be parallelized are the parts that release the GIL)
In this chapter we will learn about Cython's multithreading features to access thread-based parallelism. Our focus will be on the prange Cython function, which allows us to easily transform serial for loops to use multiple threads and tap into all available CPU cores.
Cython is a programming language that blends Python with the static type system of C and C++. cython is a compiler that translates Cython source code into efficient C or C++ source code. This source can then be compiled into a Python extension module or a standalone executable.
Cython has native support for most of the C++ language. Specifically: C++ objects can be dynamically allocated with new and del keywords. C++ objects can be stack-allocated.
You probably don't actually need to use nogil
. The GIL only stops multiple Python threads being run simulatenously. However, given you're using C++ threads they can quite happily run in the background irrespective of the GIL, provided they don't try to use PyObject
s or run Python code. So my suspicion is that you've misunderstood the GIL and you can get away with not thinking about it.
However, assuming that you do actually want to release it, you need to do 2 things:
Mark the C++ functions as nogil
to tell Cython that they don't need the GIL. Note that this doesn't actually release it - it just lets Cython know it isn't a problem if it is released:
cdef cppclass Rectange:
Rectangle(int, int, int, int) nogil except +
int getArea() nogil
# ...
Use with nogil:
blocks in your Cython wrapper class to mark the areas where the GIL is actually released.
cdef class PyRectangle:
# ...
def __cinit__(self, int x0, int y0, int x1, int y1):
with nogil:
self.c_rect = Rectangle(x0, y0, x1, y1)
def get_area(self):
cdef int result
with nogil:
result = self.c_rect.getArea()
return result
get_area
becomes slightly more complicated since the return statement can't exist inside the with nogil
block since it involves generating a Python object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With