Использование словаря в Cython, особенно внутри nogil

у меня есть словарь,

my_dict = {'a':[1,2,3], 'b':[4,5] , 'c':[7,1,2])

Я хочу использовать этот словарь внутри функции Cython nogil . Итак, я попытался объявить это как

cdef dict cy_dict = my_dict 

до этого этапа все в порядке.

теперь мне нужно перебрать ключи my_dict и если значения в список, переберем его. В Python это довольно просто:

 for key in my_dict:
      if isinstance(my_dict[key], (list, tuple)):
          ###### Iterate over the value of the list or tuple
          for value in list:
               ## Do some over operation.

но, внутри Cython, я хочу реализовать то же самое, что и внутри nogil . Поскольку объекты python не разрешены внутри ногила я застрял.

with nogil:
    #### same implementation of the same in Cython

кто-нибудь может мне помочь ?

1 ответов


единственный действительно разумный вариант-принять, что вам нужен GIL, к сожалению. Существует менее разумный вариант, включающий карты C++, но его может быть трудно применить для вашего конкретного случая.

можно использовать with gil: для повторного приобретения GIL. Здесь очевидны накладные расходы (части, использующие GIL, не могут выполняться параллельно, и может быть задержка, которую он ждет GIL). Однако, если манипуляция словарем является небольшим куском большего фрагмента кода Cython, это может не так уж плохо:

with nogil:
  # some large chunk of computationally intensive code goes here
  with gil:
    # your dictionary code
  # more computationally intensive stuff here

другой менее разумный вариант-использовать карты C++ (наряду с другими типами данных стандартной библиотеки C++). Cython может обернуть их и автоматически преобразовать их. Чтобы дать тривиальный пример, основанный на ваших данных примера:

from libcpp.map cimport map
from libcpp.string cimport string
from libcpp.vector cimport vector
from cython.operator cimport dereference, preincrement

def f():
    my_dict = {'a':[1,2,3], 'b':[4,5] , 'c':[7,1,2]}
    # the following conversion has an computational cost to it 
    # and must be done with the GIL. Depending on your design
    # you might be able to ensure it's only done once so that the
    # cost doesn't matter much
    cdef map[string,vector[int]] m = my_dict

    # cdef statements can't go inside no gil, but much of the work can
    cdef map[string,vector[int]].iterator end = m.end()
    cdef map[string,vector[int]].iterator it = m.begin()

    cdef int total_length = 0

    with nogil: # all  this stuff can now go inside nogil   
        while it != end:
            total_length += dereference(it).second.size()
            preincrement(it)

    print total_length

(вам нужно скомпилировать это с language='c++').

очевидным недостатком этого является то, что типы данных внутри dict должны быть известны заранее (это не может быть произвольный объект Python). Однако, поскольку вы не можете манипулировать произвольными объектами Python внутри nogil блок, ты все равно ограничен.