【问题标题】:Array of pointers from C++ to NumPy through Cython通过 Cython 从 C++ 到 NumPy 的指针数组
【发布时间】:2011-04-21 19:02:49
【问题描述】:

我有一个 c++ 库,我正在尝试使用 Cython 为 python 包装它。一个函数返回一个 3D 向量数组 (float (*x)[3]),我想从 python 访问该数据。我可以通过做类似的事情来做到这一点

res = [
    (self.thisptr.x[j][0],self.thisptr.x[j][1],self.thisptr.x[j][2])
    for j in xrange(self.natoms)
]

但我想以 numpy 数组的形式访问它,所以我尝试了 numpy.array ,但速度要慢得多。我也试过了

        cdef np.ndarray res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
        cdef int i
        for i in range(self.natoms):
            res[i][0] = self.thisptr.x[i][0]
            res[i][1] = self.thisptr.x[i][1]
            res[i][2] = self.thisptr.x[i][2]

但比列表版本慢大约三倍。

关于如何更快地将向量列表转换为 numpy 数组的任何建议?

完整的代码是

cimport cython
import numpy as np
cimport numpy as np


ctypedef np.float_t ftype_t
cdef extern from "ccxtc.h" namespace "ccxtc":
    cdef cppclass xtc:
        xtc(char []) except +
        int next()
        int natoms
        float (*x)[3]
        float time


cdef class pyxtc:
    cdef xtc *thisptr

    def __cinit__(self, char fname[]):
        self.thisptr = new xtc(fname)

    def __dealloc__(self):
        del self.thisptr

    property natoms:
        def __get__(self):
            return self.thisptr.natoms

    property x:
        def __get__(self):
            cdef np.ndarray res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
            cdef int i
            for i in range(self.natoms):
                res[i][0] = self.thisptr.x[i][0]
                res[i][1] = self.thisptr.x[i][1]
                res[i][2] = self.thisptr.x[i][2]
            return res
            #return [ (self.thisptr.x[j][0],self.thisptr.x[j][1],self.thisptr.x[j][2])  for j in xrange(self.natoms)]

    @cython.boundscheck(False)
    def next(self):
        return self.thisptr.next()

【问题讨论】:

  • 你尝试过 numpys frombuffer 函数吗?

标签: arrays performance numpy cython


【解决方案1】:
  1. 定义资源类型:

    cdef np.ndarray[np.float64_t, ndim=2] res = ...
    
  2. 使用完整索引:

    res[i,0] = ...
    
  3. 关闭边界检查和环绕

    @cython.boundscheck(False)
    @cython.wraparound(False) 
    

【讨论】:

    【解决方案2】:

    总结 HYRY 所说的并确保 Cython 可以生成快速索引代码,请尝试以下操作:

    cimport cython
    import numpy as np
    cimport numpy as np
    
    
    ctypedef np.float_t ftype_t
    cdef extern from "ccxtc.h" namespace "ccxtc":
        cdef cppclass xtc:
            xtc(char []) except +
            int next()
            int natoms
            float (*x)[3]
            float time
    
    
    cdef class pyxtc:
        cdef xtc *thisptr
    
        def __cinit__(self, char fname[]):
            self.thisptr = new xtc(fname)
    
        def __dealloc__(self):
            del self.thisptr
    
        property natoms:
            def __get__(self):
                return self.thisptr.natoms
    
        @cython.boundscheck(False)
        @cython.wraparound(False)
        cdef _ndarray_from_x(self):
            cdef np.ndarray[np.float_t, ndim=2] res = np.zeros([self.thisptr.natoms,3], dtype=np.float)
            cdef int i
            for i in range(self.thisptr.natoms):
                res[i,0] = self.thisptr.x[i][0]
                res[i,1] = self.thisptr.x[i][1]
                res[i,2] = self.thisptr.x[i][2]
            return res
    
        property x:
            def __get__(self):
                return self._ndarray_from_x()
    
        @cython.boundscheck(False)
        def next(self):
            return self.thisptr.next()
    

    我所做的只是将快速的东西放在 cdef 方法中,在其上放置正确的优化装饰器,然后在属性的 __get__() 中调用它。您还应该确保在range() 调用中引用self.thisptr.natoms,而不是使用natoms 属性,因为它有很多与之相关的Python 开销。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-03
      • 2023-04-01
      • 1970-01-01
      • 2018-09-13
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      相关资源
      最近更新 更多