1

Part of my works requires a lot of calculations, but they are often fairly straight-forward and can in principle quite easily be done in parallel with Cython's prange, requiring nogil. However, given that I tried to write my Cython code with a focus on having cdef classes as building blocks I encountered a problem.

Let's say I got an numerical integration routine or similar which takes a function pointer as an input

ctypedef double (* someFunctionPointer) (double tt) nogil

cdef double integrationRoutine(someFunctionfointer ff) nogil:
    # Doing something
    # ...
    return result

Now each of my integration points is actually a "larger" simulation (lots of parameters and so on I load/set/input or whatever), which is implemented in a class. So my initial approach was doing something like

cdef class SimulationClass:

    cdef double simulationFunctionPointer(SimulationClass self, double tt) nogil:
        # ...

where I though I could just hand "simulationFunctionPointer" to "integrationRoutine" and would be fine. This does of course not work because of the self argument.

All my work-arounds either require to

  • Not use cdef classes, rather something like a C struct (tricky if SimulationClass references a lot of other classes, parameters and so on)
  • Execute something with gil (because I want to work the SimulationClass; I wrote some stand alone function which took SimulationClass as a void*, but then I need to cast it to SimulationClass again, which requires the gil)

Any advice or ideas how to approach this problem? Is my first approach possible in other languages like C++?

Cheers

oli
  • 659
  • 1
  • 6
  • 18

1 Answers1

4

You can use with gil: around the blocks that need the GIL, and then with nogil: around the important inner blocks that will take most of your run time. To give a trivial example

from cython.parallel import prange

cdef class Simulation:
    cdef double some_detail

    def __cinit__(self,double some_detail):
        self.some_detail = some_detail

    def do_long_calculation(self, double v):
        with nogil:
            pass # replace pass with something long and time-consuming
        return v*self.some_detail


def run_simulations(int number_of_simulations):
    cdef int n
    for n in prange(number_of_simulations,nogil=True):
        with gil: # immediately get the gil back to do the "pythony bits"
            sim = Simulation(5.3*n)
            sim.do_long_calculation(1.2) # and release again in here"

Provided the nogil section in do_long_calculation runs from longer than the section where you set up and pass the simulations (which can run in parallel with do_long_calculation, but not with itself) this is reasonably efficient.


An additional small comment about turning a bound method into a function pointer: you really struggle to do this in Cython. The best workround I have is to use ctypes (or possibly also cffi) which can turn any Python callable into a function pointer. The way they do this appears to involve some runtime code generation which you probably don't want to replicate. You can combine this method with Cython, but it probably adds a bit of overhead to the function call (so make sure do_long_calculation is actually long!)

The following works (credit to http://osdir.com/ml/python-cython-devel/2009-10/msg00202.html)

import ctypes
# define the function type for ctypes
ftype = ctypes.CFUNCTYPE(ctypes.c_double,ctypes.c_double)

S = Simulation(3.0)
f = ftype(S.do_long_calculation) # create the ctypes function pointer

cdef someFunctionPointer cy_f_ptr = (<someFunctionPointer*><size_t>ctypes.addressof(f))[0] # this is pretty awful!
DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Addendum: I realise I didn't quite address the "function pointer" aspect of it.... if that's a problem let me know, but hopefully this makes sense for what you want to do – DavidW Jan 20 '16 at 20:49
  • This is pretty good for me, because that means I already have pretty efficient code. Basically ALL calculations are nogil functions, only to call them (i.e. use sim.do_long_calculation()) i need the gil, even though do_long_calculation() is nogil. All in all it's kinda messy though. I would hope that there was an easy way to kinda map a pointer "ctypedef double (* someFunctionPointer) (SomeClass self, double tt) nogil" to a pointer "ctypedef double (* someFunctionPointer) (double tt) nogil", so I could directly feed a method of a class into my integration routine. – oli Jan 22 '16 at 09:29
  • ctypes can turn generic Python callables into a function pointer (see https://docs.python.org/2/library/ctypes.html#callback-functions for a very quick intro). You can certainly combine it with Cython, but it's a bit messy. If you look at how they actually manage it, it's really rather complex (I think it involves generating code at runtime) and that's largely the reason you can't replicate it in Cython. You'd also struggle to avoid the `with gil:`/`with nogil:` switching that I've shown. – DavidW Jan 22 '16 at 13:35
  • Thank you very much for your answers; they seem to work. However, this would significantly complicate my code, so I decided to move away from prange/nogil/whatever for now. Personally I think the whole problem with Cython/Python object and nogil is the first bigger flaw of Cython I encountered... For the function pointer I'm not sure yet if I will go with your ctypes suggestions or some sort of wrapper function. I accepted your answer btw. – oli Jan 29 '16 at 13:03
  • @DavidW Is there an indentation error in the last line? sim.do_long_calc is within a `with gil:`-block. – The Unfun Cat Sep 03 '18 at 12:49
  • @theunfuncat No indentation error. You'll notice that I release the GIL _within_ `do_long_calc` (i.e. you can nest `with gil` and `with nogil` blocks). At this stage I can't remember exactly why I did it this way, but it's sometimes useful for functions that return Python objects – DavidW Sep 03 '18 at 13:17