簡體   English   中英

使用 nogil 對 memoryviews 進行引用計數

[英]Reference counting of memoryviews with nogil

我不太明白如何在大型/更長的 nogil 部分中使用 memoryviews 完成引用計數。 讓我們假設基本上我所有的代碼都是 nogil,除了在深處創建一個 numpy-array-to-memoryview 。 內存視圖被返回並向上使用。

一個相當簡單的例子是

import numpy as np

cdef:
    double[::1] mv
    
cdef double[::1] someFun(int nn) nogil:
    cdef:
        double[::1] mvb
    with gil:
        mvb = np.arange(nn, dtype=np.double)
    return mvb

with nogil:
    mv = someFun(30)
    # Here could be MUCH more "nogil" code
    # How is memory management/reference counting done here?

我假設當 someFun() 返回 memoryview 時,numpy 數組的引用計數仍應為 1。 Cython 之后如何處理引用計數? 我的意思是即使內存視圖/數組被取消引用,也不允許更改引用計數,對嗎? 如果上面有幾個帶有 nogil 代碼的層,它怎么知道取消對 memoryview 的引用,並且可能與 someFun() 不同,memoryview 沒有向上返回?

編輯:所以我想出了一個相當粗略的方法來做更多的測試。 我的代碼現在看起來像這樣。

import numpy as np
cdef extern from "stdio.h":
    int getchar() nogil
    int printf(const char* formatt, ...) nogil

cdef:
    double[::1] mv, mv2 = np.ones(3)
    int ii, leng = 140000000
   
cdef double[::1] someFun(int nn) nogil:
    cdef:
        double[::1] mvb
    with gil:
        mvb = np.ones(nn, dtype=np.double)
    return mvb

with nogil:
    mv = someFun(leng)
    printf("1st stop")
    getchar()
    mv = mv2
    printf("2nd stop")
    getchar()

對我來說有趣的部分是,在第 1 站仍然分配了 array/memoryview mv ,但是當我取消引用它直到第 2 站才被釋放。 我只用htop檢查了 memory 的使用情況(這就是為什么選擇這么大的數組),可能有更好的方法。 顯然,我想要發生的自由/引用計數行為,但是當它沒有 GIL 時它會這樣做很奇怪。 也許內存視圖不完全是nogil?

有人可以解釋這是否是可靠的行為嗎?

在 nogil 塊中更新 memoryview 的引用計數與您的 function someFunnogil的方式相同:它獲取 gil 來更新引用計數。

with nogil:
     mv = someFun(leng)

被翻譯成以下 C 代碼:

__pyx_t_3 = __pyx_f_3foo_someFun(__pyx_v_3foo_leng); if (unlikely(!__pyx_t_3.memview)) __PYX_ERR(0, 18, __pyx_L3_error)
__PYX_XDEC_MEMVIEW(&__pyx_v_3foo_mv, 0);
__pyx_v_3foo_mv = __pyx_t_3;
__pyx_t_3.memview = NULL;
__pyx_t_3.data = NULL;

為了綁定到新值,必須更新舊值的引用計數,這發生在__PYX_XDEC_MEMVIEW中。 它的實現可以在這里查看

static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
                                             int have_gil, int lineno) {
    ...
    } else if (likely(old_acquisition_count == 1)) {
        // Last slice => discard owned Python reference to memoryview object.
        if (have_gil) {
            Py_CLEAR(memslice->memview);
        } else {
            PyGILState_STATE _gilstate = PyGILState_Ensure();
            Py_CLEAR(memslice->memview);
            PyGILState_Release(_gilstate);
        }
    ...
}

這意味着如果我們沒有 gil ( __Pyx_XDEC_MEMVIEW調用第二個參數 = 0 ),它將被獲取以確保引用計數正確完成。

上述結果是,重新綁定 memory 視圖並不便宜,因為它需要獲取 GIL,因此應避免在緊密的 nogil 循環中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM