在 Cython 中使用字典,尤其是在 nogil 中
Using a dictionary in Cython , especially inside 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 nogil 里面不允许有物体,我都卡在这里了。
with nogil:
#### same implementation of the same in Cython
谁能帮帮我?
如果没有 GIL,您将无法使用 Python dict
,因为您可以用它做的所有事情都涉及操作 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 对象,所以您受到了很大的限制。
6 年后的附录: 我不建议将“在任何地方使用 C++ 对象”方法作为通用方法。 Cython-C++ 接口有点笨拙,您可以花很多时间来解决它。 Python 容器实际上比您想象的要好。每个人都倾向于忘记转换他们的 C++ 对象 to/from Python 对象的成本。 .
这对某些任务很有用,但在盲目地将所有 list
替换为 vector
、将 dict
替换为 map
等之前请仔细考虑。通常,如果您的 C++ 类型完全存在于您的函数中,它 可能 是一个很好的举措(但请三思而后行...)。如果它们被转换为输入或输出参数,请再考虑一下。
我有一本字典,
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 nogil 里面不允许有物体,我都卡在这里了。
with nogil:
#### same implementation of the same in Cython
谁能帮帮我?
如果没有 GIL,您将无法使用 Python dict
,因为您可以用它做的所有事情都涉及操作 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 对象,所以您受到了很大的限制。
6 年后的附录: 我不建议将“在任何地方使用 C++ 对象”方法作为通用方法。 Cython-C++ 接口有点笨拙,您可以花很多时间来解决它。 Python 容器实际上比您想象的要好。每个人都倾向于忘记转换他们的 C++ 对象 to/from Python 对象的成本。
这对某些任务很有用,但在盲目地将所有 list
替换为 vector
、将 dict
替换为 map
等之前请仔细考虑。通常,如果您的 C++ 类型完全存在于您的函数中,它 可能 是一个很好的举措(但请三思而后行...)。如果它们被转换为输入或输出参数,请再考虑一下。