从C ++到NumPy通过Cython的指针数组

时间:2011-04-21 19:02:50

标签: arrays performance numpy cython

我有一个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()

2 个答案:

答案 0 :(得分:2)

  1. 定义res的类型:

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

    res[i,0] = ...
    
  3. 关闭boundscheck&环绕

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

答案 1 :(得分:1)

总结一下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__()内调用它。您还应该确保在self.thisptr.natoms调用中引用range(),而不是使用natoms属性,该属性具有很多与之相关的Python开销。< / p>