代码之家  ›  专栏  ›  技术社区  ›  hfhc2

Cython函数指针和异常

  •  4
  • hfhc2  · 技术社区  · 6 年前

    我正在尝试使用Cython包装现有的C库。库使用回调,我想重定向回调来执行python代码。

    typedef RETCODE (*FUNC_EVAL)(int a, int b, void* func_data);
    

    其中返回代码用于表示错误。API到 创建相应的C结构如下:

    RETCODE func_create(Func** fstar,
                        FUNC_EVAL func_eval,
                        void* func_data);
    

      ctypedef RETCODE (*FUNC_EVAL)(int a,
                                    int b, 
                                    void* func_data)
    

    cdef RETCODE func_eval(int a,
                           int b,
                           void* func_data):
      (<object> func_data).func_eval(a, b)
      return OKAY;
    

    func_create

    但是,我想确保python代码中的异常 ERROR

    cdef RETCODE func_eval(int a,
                           int b,
                           void* func_data) except ERROR:
      (<object> func_data).func_eval(a, b)
      return OKAY;
    

      Cannot assign type 'RETCODE (*)(int, int, void *) except ERROR' to 'FUNC_EVAL'
    

    except ...

    1 回复  |  直到 6 年前
        1
  •  2
  •   ead    6 年前

    那是赛通试图阻止你犯一些小错误。

    首先,让我们回顾一下CPython中错误处理的工作原理:有一个全局错误状态(每个线程),它是在发生错误/异常时设置的。此状态包含有关异常类型、回溯等的信息。约定是,除了设置全局错误状态外,函数还通过一个特殊的返回值向失败发出信号,因此不必在每次函数调用后检查错误状态。

    • 如果这个函数“知道”如何处理这个错误(例如,“except”-子句),那么它必须在继续之前清除全局错误状态。

    重要的是:如果函数没有报告发生的错误,它应该清除错误状态,否则Python解释器处于不兼容状态,可能会发生细微的错误:例如cython cdef except? 取决于正确的错误状态(Cython的 except this SO-answer

    • ,cython负责全局状态:如果发生错误,则在函数返回默认值之前清除状态(并将警告写入标准错误)。
    • except 1 ,函数的调用方必须负责清除错误状态。

    FUNC_EVAL

    • ctypedef... (*FUNC_EVAL)(...) except 1
    • -函数。

    try: ... except: ... -函数,即

    cdef RETCODE func_eval(int a,
                           int b,
                           void* func_data):
      try:
        (<object> func_data).func_eval(a, b)
      except Exception:
         return ERROR
      return OKAY
    

    try... except... 即使在没有引发异常的情况下也会增加开销。这是真的。但是,您已经调用了一些Python功能,因此这个额外的开销不会影响性能。

    我的快速实验表明,如果在所谓的python功能中根本没有计算,您可能会损失高达30%(请参见答案附录中的实验)。但是上面是一个极端的情况,通常你会松得少得多,所以我不会尝试优化它,除非分析器显示它确实是一个问题。

    ERROR=0 , so you can use the implementation detail, that Cython sets the result to


    %%cython -a
    cdef extern from *:
        """
        typedef int (*FUN)(void);
        void call(FUN f){
           f();
        }
        """
        ctypedef int (*FUN)()
        void call(FUN f)
    
    def dummy():
        pass
    
    cdef int cython_handling():
        dummy()
        return 1
    
    cdef int manual_handling():
        try:
            dummy()
        except Exception:
            return 0
        return 1
    
    def check_cython():
        cdef int i
        for i in range(1000):
            call(cython_handling)
    
    def check_manually():
        cdef int i
        for i in range(1000):
            call(manual_handling)
    

    %timeit check_cython()
    # 21.6 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    %timeit check_manually()
    # 27 µs ± 493 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    推荐文章