我正在看一個視頻來學習 Numba。在 17:00,演示者在螢屏上顯示以下代碼:
@njit
def simulate_spring_mass_funky_damper(x0, T=10, dt=0.0001, vt=1.0):
times = np.arange(0, T, dt)
positions = np.zeros_like(times)
v = 0
a = 0
x = x0
positions[0] = x0/x0
for ii in range(len(times)):
if ii == 0:
continue
t = times[ii]
a = friction_fn(v, vt) - 100*x
v = v a*dt
x = x v*dt
positions[ii] = x/x0
return times, positions
然后演示者繼續指示 numba 使用jnit(nogil=True). 他的論點是
此函式在運行時不需要訪問 python 解釋器。事實上,我們已經確定了這一點。所以我們可以另外告訴它釋放 GIL
然后演示者在多執行緒中使用此代碼:
%%time
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(8) as ex:
ex.map(simulate_spring_mass_funky_damper, np.arange(0, 1000, 0.1))
我知道希望與 python 解釋器互動的執行緒需要 GIL。如果代碼不需要與 python 解釋器互動,那么發布或不發布 GIL 不是沒有意義嗎?
- 我不明白這個函式怎么不需要python解釋器?
- 如果代碼不需要與 GIL 互動,那么發布 GIL 的意義何在?無論如何它都不會與 python 解釋器互動,并且 GIL 不會發揮作用
uj5u.com熱心網友回復:
我不明白這個函式怎么不需要python解釋器?
Numba 使用 JIT 編譯器 (LLVM-Lite) 將 Python 代碼轉換為可以在解釋器背景關系中運行的快速二進制檔案。當函式使用@njit裝飾器(或等價物@jit(nopython=True))時,Numba 在內部生成兩個函式:一個是將輸入的純 Python 物件轉換為內部本機型別的包裝器,另一個將執行實際計算(輸出值由然后包裝函式)。問題是像 CPython 串列或字典這樣的物件需要使用 CPython 受 GIL 保護,但內部 Numba 本機值不需要. Numba 將 CPython 整數物件轉換為低級固定大小的整數,串列/字典和 Numpy 陣列被輸入并轉換為獨立于 CPython 的內部資料結構。但是,此方法不適用于所有 CPython 物件:在釋放 GIL 時,Numba 無法在 CPython 物件上作業。
Numpy 陣列的情況有點特殊,因為 Numpy 被設計為當您使用本機陣列(僅使用它們)時可以釋放 GIL 。包含 CPython 物件的 Numpy 陣列需要 GIL。另請注意,Numba 不直接使用 Numpy CPython API。它在內部重新實作了 Numpy。
對于串列和字典,Numba 將整個資料結構復制到另一個不需要 GIL 的結構中。不過,這僅適用于全型別串列/字典。包裝函式將在將型別串列/字典轉換回 CPython 的時候。這種手術很昂貴。
簡而言之:在您的情況下,Numba 生成的包裝函式需要 GIL,因為它適用于CPython 物件,但實際計算功能確實需要 GIL。因此,指定nogil=True使包裝函式在呼叫計算函式之前釋放 GIL(然后在計算函式執行后重新獲取 GIL)。
如果代碼不需要與 GIL 互動,那么發布 GIL 的意義何在?無論如何它都不會與 python 解釋器互動,并且 GIL 不會發揮作用
好吧,如果您使用@njit裝飾器(或等效物@jit(nopython=True)),那么nogil=True如果代碼是按順序呼叫的,那么指定通常是無用的,因為無論如何都不能使用 GIL(因為在這種情況下,Numba“生成不訪問 Python C API 的代碼”)到檔案)。
但是,在您的情況下,有許多執行緒呼叫 Numba 函式。如果生成的包裝函式沒有釋放 GIL,那么 Numba 計算函式將被串行執行(由于執行緒之間的背景關系切換代價高昂,因此效率低于順序代碼)。釋放 GIL 解決了這個問題。請注意,由于 GIL(由于 CPython 物件轉換而需要),包裝代碼仍然是串行執行的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/389938.html
