在Cython中使用字典,尤其是在nogil内部

发布于 2021-01-29 15:06:22

我有一本字典,

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内部实现相同的功能。由于nogil内不允许使用python对象,因此我全都陷入了困境。

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

有人可以帮我吗?

关注者
0
被浏览
69
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    dict没有GIL,就不能使用Python ,因为您可以做的所有事情都涉及到操作Python对象。您最明智的选择是接受您需要的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对象)。但是,由于您无法在nogil块内操作任意Python对象,因此无论如何还是受到很大限制。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看