Reputation: 8630
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
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