seb007
seb007

Reputation: 568

Cython overhead of numpy arrays

I wrote a simple interpolation function in cython, to be called (a lot) from other cython code. One of the parameters is a numpy array:

@cython.boundscheck(False) 
@cython.cdivision(True)
@cython.wraparound(False) 
cdef double interpU_cython(double kX,double kY,int iX,int iY, int iTheta,int nbX,int nbY,np.ndarray[double, ndim=3] u, double outVal):
    cdef double uPt, u0, u1
    if (iX >= 0 and iY >= 0 and iX < nbX-1 and iY < nbY-1):
        u0 = u[iX,iY,iTheta] + (u[iX+1,iY,iTheta]-u[iX,iY,iTheta]) * kX
        u1 = u[iX,iY+1,iTheta] + (u[iX+1,iY+1,iTheta]-u[iX,iY+1,iTheta]) * kX
        uPt = u0 + (u1-u0) * kY
    else:
        uPt = outVal
    return uPt

I checked the python calls with cython -a, and it looks like the function call relies on several python calls:

+01: cimport cython
 02: cimport numpy as np
+03: import numpy as np
 04: 
 05: @cython.boundscheck(False)
 06: @cython.cdivision(True)
 07: @cython.wraparound(False)
+08: cdef double interpU_cython(double kX,double kY,int iX,int iY, int iTheta,int nbX,int nbY,np.ndarray[double, ndim=3] u, double outVal):
static double __pyx_f_10FSM_cython_interpU_cython(double __pyx_v_kX, double __pyx_v_kY, int __pyx_v_iX, int __pyx_v_iY, int __pyx_v_iTheta, int __pyx_v_nbX, int __pyx_v_nbY, PyArrayObject *__pyx_v_u, double __pyx_v_outVal) {
  double __pyx_v_uPt;
  double __pyx_v_u0;
  double __pyx_v_u1;
  __Pyx_LocalBuf_ND __pyx_pybuffernd_u;
  __Pyx_Buffer __pyx_pybuffer_u;
  double __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("interpU_cython", 0);
  __pyx_pybuffer_u.pybuffer.buf = NULL;
  __pyx_pybuffer_u.refcount = 0;
  __pyx_pybuffernd_u.data = NULL;
  __pyx_pybuffernd_u.rcbuffer = &__pyx_pybuffer_u;
  {
    __Pyx_BufFmt_StackElem __pyx_stack[1];
    if (unlikely(__Pyx_GetBufferAndValidate(&__pyx_pybuffernd_u.rcbuffer->pybuffer, (PyObject*)__pyx_v_u, &__Pyx_TypeInfo_double, PyBUF_FORMAT| PyBUF_STRIDES, 3, 0, __pyx_stack) == -1)) __PYX_ERR(0, 8, __pyx_L1_error)
  }
  __pyx_pybuffernd_u.diminfo[0].strides = __pyx_pybuffernd_u.rcbuffer->pybuffer.strides[0]; __pyx_pybuffernd_u.diminfo[0].shape = __pyx_pybuffernd_u.rcbuffer->pybuffer.shape[0]; __pyx_pybuffernd_u.diminfo[1].strides = __pyx_pybuffernd_u.rcbuffer->pybuffer.strides[1]; __pyx_pybuffernd_u.diminfo[1].shape = __pyx_pybuffernd_u.rcbuffer->pybuffer.shape[1]; __pyx_pybuffernd_u.diminfo[2].strides = __pyx_pybuffernd_u.rcbuffer->pybuffer.strides[2]; __pyx_pybuffernd_u.diminfo[2].shape = __pyx_pybuffernd_u.rcbuffer->pybuffer.shape[2];
/* … */
  /* function exit code */
  __pyx_L1_error:;
  { PyObject *__pyx_type, *__pyx_value, *__pyx_tb;
    __Pyx_PyThreadState_declare
    __Pyx_PyThreadState_assign
    __Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);
    __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_u.rcbuffer->pybuffer);
  __Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}
  __Pyx_WriteUnraisable("FSM_cython.interpU_cython", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
  __pyx_r = 0;
  goto __pyx_L2;
  __pyx_L0:;
  __Pyx_SafeReleaseBuffer(&__pyx_pybuffernd_u.rcbuffer->pybuffer);
  __pyx_L2:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 09:     cdef double uPt, u0, u1
+10:     if (iX >= 0 and iY >= 0 and iX < nbX-1 and iY < nbY-1):
+11:         u0 = u[iX,iY,iTheta] + (u[iX+1,iY,iTheta]-u[iX,iY,iTheta]) * kX
+12:         u1 = u[iX,iY+1,iTheta] + (u[iX+1,iY+1,iTheta]-u[iX,iY+1,iTheta]) * kX
+13:         uPt = u0 + (u1-u0) * kY
 14:     else:
+15:         uPt = outVal
+16:     return uPt

Is there an efficient way to pass and use numpy arrays with no significant overhead, or should I just use c arrays for everything in the c compiled parts of the code?

Is it considered safe to use the pointer to the numpy array first element and add array size in the parameters to use it as a one-dimensional array?

Thanks

Upvotes: 1

Views: 866

Answers (1)

Pierre de Buyl
Pierre de Buyl

Reputation: 7293

Have a look at https://jakevdp.github.io/blog/2012/08/08/memoryview-benchmarks/

This blog post compares several possibilities to use NumPy arrays in Cython.

The short answer is that you should use typed memoryviews that are declared as double[:,:,:] u instead of np.ndarray[double, ndim=3] u. Docs: http://docs.cython.org/en/latest/src/userguide/memoryviews.html

Edit: you can query the shape of a memoryview

Upvotes: 2

Related Questions