如何防止Cython中的__Pyx_MemoryView_Len(__ pyx_v_a)Python交互

时间:2018-12-09 21:49:01

标签: python cython

我有一个简单的Cython函数,该函数获取memoryview的长度:

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

我正在使用annotate=True指令编译代码,以便让我了解Cython在哪里与Python有交互作用。

生成的html在return len(a)行中包含以下文本:

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

这种Python交互应该很慢。我有办法防止Python交互吗?我已经尝试过a.shape[0],但这没有帮助。

1 个答案:

答案 0 :(得分:2)

您不必担心,__Pyx_MemoryView_Len会变得如此快,因为it is defined如下:

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])

该行的颜色不是黄色,而是淡黄色-这主要意味着它不会产生纯C代码,而是使用了一些_Pyx_XXX功能,这通常对性能没有不良影响完全没有。

没有Python交互作用-经过C预处理程序传递后,C编译器将看到它,就好像您已经写过:

return a.shape[0]

这会导致白线。

如果您也对多维内存视图的大小感兴趣,那么this SO-question可能值得一读。


注释以黄线显示函数的定义。但是,只有模块加载时的成本(并且一次支付,而不是每次调用函数都支付)才是“黄色”颜色。

看看生成的C代码:

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仅在使用CYTHON_REFNANNY-defined.构建时有效

annotate工具还显示了另一个代码,它对应于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;

但是,此代码是__pyx_pymod_exec_XXXXX / PyInit_XXXXX的一部分,并且在加载模块时仅被调用一次。实际上,我不确定这与get_length有何关系以及为什么需要这样做,但是由于成本如此之小,所以我从来没有足够地去寻找答案。