Ginger
Ginger

Reputation: 8630

How to prevent a __Pyx_MemoryView_Len(__pyx_v_a) Python Interaction in Cython

I have a simple Cython function, which gets the length of a memoryview:

cdef int get_length(int[:] a):
    return len(a)

I am compiling the compiling the code with the annotate=True directive, to let me see where Cython has some Python interaction.

The resulting html contains this text, for the return len(a) line:

  __pyx_t_1 = __Pyx_MemoryView_Len(__pyx_v_a); 
  __pyx_r = __pyx_t_1;
  goto __pyx_L0;

Such Python interaction is supposed to be slow. Is there a way for me to prevent the Python interaction? I have tried a.shape[0], but that did not help.

Upvotes: 3

Views: 295

Answers (1)

ead
ead

Reputation: 34326

You should not worry about that, __Pyx_MemoryView_Len is as fast as it gets, because it is defined as:

typedef struct {
  struct {{memview_struct_name}} *memview;
  char *data;
  Py_ssize_t shape[{{max_dims}}];
  ...
} {{memviewslice_name}};

// used for "len(memviewslice)"
#define __Pyx_MemoryView_Len(m) (m.shape[0])

The color of this line isn't yellow but yellowish - which mostly means that this it results not in pure C-code, but that some _Pyx_XXX functionality is used, which often isn't bad for the performance at all.

There is no Python-interaction - after the C-preprocessor-pass, it will be seen by the C-compiler as if you have written:

return a.shape[0]

which leads to a white line by the way.

If you are also interested in the size of a multi-dimensional memview, then this SO-question might be worth reading.


Annotate shows the definition of the function as a yellow line. However, only the cost when module is loaded (and it is paid once and not every time the function is called) is responsible for the "yellow" color.

Take a look at the resulting C-code:

static int __pyx_f_9my_module_get_length(__Pyx_memviewslice __pyx_v_a) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("get_length", 0);

  /* "my_module.pyx":5
 * 
 * cdef int get_length(int[:] a):
 *     return a.shape[0]             # <<<<<<<<<<<<<<
 */
  __pyx_r = (__pyx_v_a.shape[0]);
  goto __pyx_L0;

  /* "my_module.pyx":4
 *     return 3. * a
 * 
 * cdef int get_length(int[:] a):             # <<<<<<<<<<<<<<
 *     return a.shape[0]
 */

  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

RefNannyXXXX is only active when build with CYTHON_REFNANNY-defined.

There is also another code shown by the annotate-tool, which corresponds to cdef int get_length(int[:] a): :

/* … */
  __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 1, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(1, 1, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

However, this code is part of __pyx_pymod_exec_XXXXX/PyInit_XXXXX and which is only called once, when the module is loaded. Actually, I'm not sure how this is related to get_length and why this is needed, but because the costs are so small I never cared enough to find out.

Upvotes: 4

Related Questions