我需要在 Cython 中使用 `nogil` 吗
Do I need to use `nogil` in Cython
我有一些 Cython 代码,我想尽快 运行。我需要释放 GIL 才能执行此操作吗?
假设我的代码与此类似:
import numpy as np
# trivial definition just for illustration!
cdef double some_complicated_function(double x) nogil:
return x
cdef void func(double[:] input) nogil:
cdef double[:] array = np.zeros_like(input)
for i in range(array.shape[0]):
array[i] = some_complicated_function(input[i])
我从 np.zeros_like
行收到一大堆错误消息,类似于:
nogilcode.pyx:7:40: Calling gil-requiring function not allowed without gil
nogilcode.pyx:7:29: Accessing Python attribute not allowed without gil
nogilcode.pyx:7:27: Accessing Python global or builtin not allowed without gil
nogilcode.pyx:7:40: Constructing Python tuple not allowed without gil
nogilcode.pyx:7:41: Converting to Python object not allowed without gil
我是否需要找到一种无需 GIL 即可调用 np.zeros_like
的方法?或者寻找其他不需要 GIL 的数组分配方式?
注意:这是一个自我回答的问题,旨在消除对 Cython 和 GIL 的一些常见误解(当然,我们也欢迎您回答!)。
第二个注意事项:我已经为 Cython 做出了足够多的贡献,所以我应该在这里注明(考虑到我要提出这个话题)
否 - 您可能不需要 发布 GIL。
GIL(全局解释器锁)的基本功能是确保Python的内部机制不受竞争条件的影响,通过确保只有一个Python线程能够运行一次。然而,仅仅持有 GIL 并不会降低你的代码速度。
你应该释放 GIL 的两个(相关)场合是:
使用 Cython's parallelism mechanism。例如 prange
循环的内容必须是 nogil
.
如果您希望其他(外部)Python 线程能够同时运行。
一个。如果你有一个不需要 GIL 的大 computationally/IO-intensive 块,那么释放它可能是“礼貌的”,只是为了让你的代码用户想要进行多线程处理。但是,这主要是有用的,而不是必需的。
b。 (非常、非常偶尔)有时用一个简短的 with nogil: pass
块来短暂释放 GIL 是有用的。这是因为 Cython 不会自发释放它(与 Python 不同),因此如果您正在等待另一个 Python 线程来完成任务,这可以避免死锁。除非您使用 Cython 编译 GUI 代码,否则这个子点可能不适用于您。
在没有 GIL 的情况下 运行 的 Cython 代码(不调用 Python,纯粹的 C 级数字操作)是 经常 一种 运行 有效的代码。这有时会给人这样的印象,即反过来是正确的,诀窍是释放 GIL,而不是他们 运行ning 的实际代码。不要被这个误导 - 你的(单线程)代码将 运行 使用或不使用 GIL 的速度相同。
因此,如果您有一个很好的快速 Numpy 函数,它可以在大块数据上快速完成您想要的操作,但只能使用 GIL 调用,那么就调用它 - 没有坏处!
最后一点:即使在 nogil
块中(例如 prange
循环),如果需要,您也可以随时取回 GIL:
with gil:
... # small block of GIL requiring code goes here
尽量不要经常这样做(getting/releasing 这需要时间,当然一次只能有一个线程 运行ning 这个块)但同样这是做小事情的好方法Python 需要的操作。
我有一些 Cython 代码,我想尽快 运行。我需要释放 GIL 才能执行此操作吗?
假设我的代码与此类似:
import numpy as np
# trivial definition just for illustration!
cdef double some_complicated_function(double x) nogil:
return x
cdef void func(double[:] input) nogil:
cdef double[:] array = np.zeros_like(input)
for i in range(array.shape[0]):
array[i] = some_complicated_function(input[i])
我从 np.zeros_like
行收到一大堆错误消息,类似于:
nogilcode.pyx:7:40: Calling gil-requiring function not allowed without gil
nogilcode.pyx:7:29: Accessing Python attribute not allowed without gil
nogilcode.pyx:7:27: Accessing Python global or builtin not allowed without gil
nogilcode.pyx:7:40: Constructing Python tuple not allowed without gil
nogilcode.pyx:7:41: Converting to Python object not allowed without gil
我是否需要找到一种无需 GIL 即可调用 np.zeros_like
的方法?或者寻找其他不需要 GIL 的数组分配方式?
注意:这是一个自我回答的问题,旨在消除对 Cython 和 GIL 的一些常见误解(当然,我们也欢迎您回答!)。
第二个注意事项:我已经为 Cython 做出了足够多的贡献,所以我应该在这里注明(考虑到我要提出这个话题)
否 - 您可能不需要 发布 GIL。
GIL(全局解释器锁)的基本功能是确保Python的内部机制不受竞争条件的影响,通过确保只有一个Python线程能够运行一次。然而,仅仅持有 GIL 并不会降低你的代码速度。
你应该释放 GIL 的两个(相关)场合是:
使用 Cython's parallelism mechanism。例如
prange
循环的内容必须是nogil
.如果您希望其他(外部)Python 线程能够同时运行。
一个。如果你有一个不需要 GIL 的大 computationally/IO-intensive 块,那么释放它可能是“礼貌的”,只是为了让你的代码用户想要进行多线程处理。但是,这主要是有用的,而不是必需的。
b。 (非常、非常偶尔)有时用一个简短的
with nogil: pass
块来短暂释放 GIL 是有用的。这是因为 Cython 不会自发释放它(与 Python 不同),因此如果您正在等待另一个 Python 线程来完成任务,这可以避免死锁。除非您使用 Cython 编译 GUI 代码,否则这个子点可能不适用于您。
在没有 GIL 的情况下 运行 的 Cython 代码(不调用 Python,纯粹的 C 级数字操作)是 经常 一种 运行 有效的代码。这有时会给人这样的印象,即反过来是正确的,诀窍是释放 GIL,而不是他们 运行ning 的实际代码。不要被这个误导 - 你的(单线程)代码将 运行 使用或不使用 GIL 的速度相同。
因此,如果您有一个很好的快速 Numpy 函数,它可以在大块数据上快速完成您想要的操作,但只能使用 GIL 调用,那么就调用它 - 没有坏处!
最后一点:即使在 nogil
块中(例如 prange
循环),如果需要,您也可以随时取回 GIL:
with gil:
... # small block of GIL requiring code goes here
尽量不要经常这样做(getting/releasing 这需要时间,当然一次只能有一个线程 运行ning 这个块)但同样这是做小事情的好方法Python 需要的操作。