首页 文章

如何在Cython中阻止__Pyx_MemoryView_Len(__ pyx_v_a)Python交互

提问于
浏览
2

我有一个简单的Cython函数,它获取内存视图的长度:

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 回答

  • 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-preprocessor-pass之后,C编译器会看到它,好像你已经写过:

    return a.shape[0]
    

    顺便说一句,这会导致一条白线 .

    如果您对多维memview的大小也感兴趣,那么this SO-question可能值得一读 .


    Annotate将函数的定义显示为黄线 . 但是,只有加载模块时的成本(并且它被支付一次而不是每次调用函数时)都是“黄色”颜色的原因 .

    看看生成的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 -tool还显示了另一个代码,对应于 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 有何关系以及为什么需要这样做,但由于成本太低,我从不关心这一点 .

相关问题