[英]How/When to use a Cython Extension Type vs a Cython Struct to store data that is passed around class functions
我有興趣創建一個數據結構來保存傳遞給不同函數的數據/信息。 我們目前的做法是如下代碼:
# right now we use a C-struct to hold data that is passed
# around in a separate class 'A'
cdef struct Record:
double threshold
double improvement
cdef class A:
cpdef py_dostuff(self):
cdef Record record
record.threshold = new_threshold
record.improvement = new_improvement
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement)
這使用了一個 C 風格的結構,不幸的是它不支持繼承,所以如果我們想用另一個使用該結構“子類”的類“B”來子類化“A”,它是行不通的。 我嘗試使用課程沒有用。 理想情況下,我可以在不犧牲性能的情況下執行以下操作。 我的想法是,應該可以用純 Cython 擴展類型替換結構,因為我只使用 C 級的東西,但擴展類型將使我能夠子類化Record
和A
。
# now, I would like to use a Cython extension type to hold data that is passed
# around in a separate class 'A'
cdef class Record:
cdef double threshold
cdef double improvement
cdef class A:
cpdef py_dostuff(self):
cdef Record record
record.threshold = new_threshold
record.improvement = new_improvement
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement)
# The reason I would like to use a Cython extension type is that it can then support clean inheritance of the data structure
cdef class NewRecord(Record):
cdef double threshold
cdef double improvement
cdef int new_attribute
# E.g. a new subclass of 'A' would still work even if all we did was extend the logic to a "NewRecord"
cdef class B(A):
cpdef py_dostuff(self):
cdef NewRecord record
record.threshold = new_threshold
record.improvement = new_improvement
record.new_attribute = new_attribute
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement, record.new_attribute)
我的問題是:
如何正確替換結構的純 Cython 類(沒有 Python 對象以允許 nogil 操作)?
使用 Cython cdef class
代替結構沒有什么特別棘手的——您可以將它們傳遞給nogil
函數並訪問它們的非object
cdef
屬性,而無需 GIL:
cdef class A:
cdef double threshold
cdef double improvement
def example_func(self):
with nogil:
self.threshold = do_something(self)
cdef double do_something(A a) nogil:
return a.threshold
請考慮您是否真的需要在沒有 GIL 的情況下工作(即您正在執行多線程)。 很多人都喜歡“nogil==fast”,並且主要出於貨物崇拜的原因要求 nogil 解決方案。
請注意,您不能使用cdef class
的地址。 在您的(非工作)示例中, cy_dostuff(&record)
將變為cy_dostuff(record)
。
會有性能差異嗎?
可能不多。 cdef class
本質上是一個結構。 在內部通過指針(而不是通過值)分配並在堆上分配,因此可能會產生很小的差異。 不過,Cython 會為您處理細節。
如果我做不到,為什么不呢?有什么解決方法可以傳遞類似結構的數據結構?
你可以做“組合繼承”:
cdef struct Record:
double threshold
double improvement
cdef struct NewRecord:
Record base
int new_attribute
C 標准明確允許在Record
和NewRecord
指針之間進行轉換以支持這種確切的使用。 因此,如果您有一個采用Record
指針的函數,您可以執行f(<Record*>&my_new_record)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.