簡體   English   中英

Cython:了解具有間接連續內存布局的類型化內存視圖

[英]Cython: understanding a typed memoryview with a indirect_contignuous memory layout

我想更多地了解 Cython 的typed-memoryviews和內存布局indirect_contiguous

根據文檔,當“指針列表是連續的”時使用indirect_contiguous

還有一個示例用法:

# contiguous list of pointers to contiguous lists of ints
cdef int[::view.indirect_contiguous, ::1] b

因此,如果我錯了,請糾正我,但我假設“指向連續整數列表的連續指針列表”意味着類似於由以下 C++ 虛擬代碼創建的數組:

// we want to create a 'contiguous list of pointers to contiguous lists of ints'

int** array;
// allocate row-pointers
// This is the 'contiguous list of pointers' related to the first dimension:
array = new int*[ROW_COUNT]

// allocate some rows, each row is a 'contiguous list of ints'
array[0] = new int[COL_COUNT]{1,2,3}

因此,如果我理解正確,那么在我的 Cython 代碼中應該可以像這樣從int**獲取內存視圖:

cdef int** list_of_pointers = get_pointers()
cdef int[::view.indirect_contiguous, ::1] view = <int[:ROW_COUNT:view.indirect_contiguous,COL_COUNT:1]> list_of_pointers

但是我得到編譯錯誤:

cdef int[::view.indirect_contiguous, ::1] view = <int[:ROW_COUNT:view.indirect_contiguous,:COL_COUNT:1]> list_of_pointers
                                                                                                        ^                                                                                                                              
------------------------------------------------------------

memview_test.pyx:76:116: Pointer base type does not match cython.array base type

我做錯了什么? 我是否遺漏了任何演員表,或者我是否誤解了 indirect_contiguous 的概念?

讓我們澄清一下:類型化內存視圖只能用於實現緩沖區協議的對象。

原始 C 指針顯然沒有實現緩沖區協議。 但是您可能會問,為什么像下面的快速代碼這樣的代碼可以工作:

%%cython    
from libc.stdlib cimport calloc
def f():
    cdef int* v=<int *>calloc(4, sizeof(int))
    cdef int[:] b = <int[:4]>v
    return b[0] # leaks memory, so what?

此處,指針 ( v ) 用於構造類型化內存視圖 ( b )。 然而,在幕后還有更多內容(如在 cythonized c 文件中所見):

  • 構造了一個cython 數組(即cython.view.array ),它包裝了原始指針並可以通過 buffer-protocol 公開它
  • 該數組用於創建類型化內存視圖。

您對view.indirect_contiguous的用途的理解是正確的——這正是您想要的。 然而,問題是view.array ,它無法處理這種類型的數據布局。

view.indirectview.indirect_contiguous對應於協議緩沖區用語中的PyBUF_INDIRECT ,為此,字段suboffsets必須包含一些有意義的值(即對於某些維度>=0 )。 但是,正如在源代碼view.array中看到的那樣,根本沒有這個成員——它根本無法表示復雜的內存布局!

它把我們留在哪里? 正如@chrisb 和@DavidW 在您的其他問題中指出的那樣,您將必須實現一個包裝器,它可以通過協議緩沖區公開您的數據結構。

Python 中有一些數據結構使用間接內存布局——最突出的是 PIL 數組。 理解suboffsets量應該如何工作的一個很好的起點是這篇文檔

void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
                       Py_ssize_t *suboffsets, Py_ssize_t *indices) {
    char *pointer = (char*)buf;    // A
    int i;
    for (i = 0; i < ndim; i++) {
        pointer += strides[i] * indices[i]; // B
        if (suboffsets[i] >=0 ) {
            pointer = *((char**)pointer) + suboffsets[i];  // C
        }
    }
    return (void*)pointer;  // D
}

在你的情況下, stridesoffsets

  • strides=[sizeof(int*), sizeof(int)] (即通常的x86_64機器上的[8,4]
  • offsets=[0,-1] ,即只有第一個維度是間接的。

獲取元素[x,y]的地址將發生如下情況:

  • 在行A中, pointer設置為buf ,讓我們假設BUF
  • 第一維:
    • 在行B中, pointer變為BUF+x*8 ,並指向指向第 x 行的指針的位置。
    • 因為suboffsets[0]>=0 ,我們取消引用C行中的指針,因此它顯示地址ROW_X - 第 x 行的開頭。
  • 第二維:
    • B行中,我們使用strides獲取y元素的地址,即pointer=ROW_X+4*y
    • 第二維是直接的(由suboffset[1]<0發出信號),因此不需要解除引用。
  • 我們完成了, pointer指向所需的地址並在D行返回。

FWIW,我已經實現了一個庫,它能夠通過緩沖協議導出int**和類似的內存布局: https ://github.com/realead/indirect_buffer。

暫無
暫無

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

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