【问题标题】:How to Access a C struct internal data which have fixed size array from Python?如何从 Python 访问具有固定大小数组的 C 结构内部数据?
【发布时间】:2018-11-20 06:02:04
【问题描述】:

我知道这是重复 Writing Cython extension: how to access a C struct internal data from Python?

但是我还没有找到任何资源来处理这样的 C 结构,

ctypedef struct IoRegAccess:        
    int Addr[26]             
    int Data[26]             
    int AddrLen              
    int DataLen

使用__getitem__/__setitem__ 方法我们可以访问这个结构数组,但我有兴趣在特定类中使用@property 来完成

  cdef class PyIoRegAccess:
      cdef IoRegAccess RegContainer

      #ctor of this class
      def __cinit__(self):
          memset(self.RegContainer.uiAddr, 0,sizeof(uint32_t)*26)
          memset(self.RegContainer.uiData, 0,sizeof(uint32_t)*26)


      @property                                 
      def uiAddr(self,key):                         
          return self.RegContainer.uiAddr[key]       

      @uiAddr.setter                            
      def uiAddr(self, key, value):             
          self.RegContainer.uiAddr[key] = value 

现在我有两个错误,

特殊方法 __get__ 的参数数量错误(声明 2 个,预期 1 个)

特殊方法 __set__ 的参数数量错误(声明 3 个,预期 2 个)

请对此提出建议

【问题讨论】:

  • 你的缩进看起来不对。
  • 忽略缩进...它只是发布的代码。内部编辑器,配合完美缩进
  • 我已经更正了发布代码中的缩进

标签: python cython


【解决方案1】:

您似乎正试图以不适合的方式使用属性。可以在您的场景中使用属性,但从性能的角度来看,这可能并不明智,因为这个答案将在下面进一步显示。

在纯 Python 中,您可以使用(您肯定已经知道)如下属性来访问列表中的元素:

def class A:
   def __init__(self):
       self._lst=[1,2,3,4]
   @property
   def lst(self):
       return self._lst

即属性不用于访问列表的元素,而是用于访问列表本身。

现在

a=A()
a.lst          # accesses list via property
a.lst[0] = 10  # accesses list via property + __getitem__ of the list
               # no property-setter needed, we don't set lst-property, 
               # just an element of the list

a.lst[0]       # returns 10

天真翻译成 Cython 的相同想法将是(您的示例有些简化):

%%cython
from libc.string cimport memset

cdef class CyA:
      cdef int _Addr[26]

      def __cinit__(self):
          memset(self._Addr, 0,sizeof(int)*26)

      @property                                 
      def Addr(self): 
          return self._Addr  

但是,这并不像人们预期的那样工作:

a=CyA()
a.Addr[0] = 10 

a.Addr[0]      # returns 0!

问题是,在幕后 Cython 将 int C 数组转换为一个列表(多么大的开销!)并且更改 Addr-array 的这个副本根本不会更改原始数组。

您需要在属性中返回数组_Addr 的(类型化)内存视图:

%%cython
....
      @property                                 
      def Addr(self): 
          cdef int[:] memview = self._Addr
          return memview

按预期工作:

a=CyA()
a.Addr[0] = 10 

a.Addr[0]      # returns 10, as expected!

您可能担心只为一次访问创建内存视图的开销(您是对的),在这种情况下,可以缓存创建的内存视图并一遍又一遍地重用它:

%%cython

cdef class CyA:
      cdef int _Addr[26]
      cdef int[:] memview_cache
      def __cinit__(self):
          memset(self._Addr, 1204,sizeof(int)*26)
          self.memview_cache = None

  ...
  @property                                 
  def Addr_fast(self): 
      if self.memview_cache is None:
          self.memview_cache = self._Addr
      return self.memview_cache

导致因子3的加速:

a=CyA()
%timeit a.Addr               # 1.05 µs ± 36.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit a.Addr_fast          # 328 ns ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

然而,与通过__setitem__ 进行非花哨和直接的元素设置相比,这仍然是太多开销:

%%cython
cdef class CyA:
      cdef int _Addr[26]
      ...
      def __setitem__(self, index, int value):
          self._Addr[index] = value

导致

a=CyA()
%timeit a.Addr_fast[0] = 10          # 483 ns ± 22.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit a[0] = 10                    # 32.5 ns ± 0.669 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

大约快 10 倍!

【讨论】:

  • 使用 setitem 方法的唯一问题是我需要在类对象周围使用 [] 运算符,例如 a[0] = 10 并且通过使用属性我可以使用结构成员名称使用 .像“a.Addr_fast[0]”这样的运算符。是否可以使用 setitem__/__getitem 来做同样的事情?
  • @BhoomilChavda 我不知道您如何使用 setitem 来实现它。您可以提供set_fieldX(index) 函数,它比 setitem 稍慢,但仍比属性快。您可以保留多少性能取决于您。
  • 最后一个问题@ead:这次你测量的是哪个CPU?如果它低于 corei7,那么在使用缓存版本时它会给我更多的小数字
  • @BhoomilChavda 它是 i7。可能不是绝对时间是相关的,而是时间之间的关系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 2017-05-07
  • 2019-01-11
相关资源
最近更新 更多