用cython

时间:2017-11-08 17:10:44

标签: python numpy cython

我正在尝试加速我使用cython的算法。我已经按照this page上的大多数禁令来采用我的原始功能并将其转换为优化形式,但我似乎所做的只是放慢速度。我已经非常彻底地检查了这些类型,甚至查看了生成的C代码,我也进行了一些行分析,这表明花了很多时间来比较LONG类型,但除此之外我还没有运气好的话。我注意到内联似乎是最昂贵的循环。

我刚刚遇到一些非常紧张的情况?我发现很难相信cython无法优化这段代码,因此我认为某些地方的输入错误。

我的测试目录如下所示

cython_test
  - test_package
    - cython_fail
      - __init__.py
      - test_extension.pyx
    - setup.py
  - profiling.py

单个文件如下所示:

初始化脚本

#__init__.py
__all__ = []
__version__ = 0.1
from . import test_extension  

核心代码

# test_extension.pyx
import numpy as np

def fun(lattice, default_val = -1, leave_bar = True):
    if len(lattice.shape) != 2 or not np.all(
        np.logical_or(lattice == 0, lattice == 1)):
        raise RuntimeError(
            "Function only defined for 2D binary grid!")

    clusters = default_val*np.ones_like(lattice, dtype = DTYPE)
    ids = np.reshape(
        np.arange(
            1, lattice.shape[0]*lattice.shape[1] + 1
        ),
        lattice.shape)

    for i in range(lattice.shape[0]):
        for j in range(lattice.shape[1]):
            if lattice[i, j]:
                left_occ = lattice[i-1, j] if i != 0 else 0
                up_occ = lattice[i, j-1] if j != 0 else 0

                if not left_occ and not up_occ:
                    clusters[i, j] = ids[i, j]
                elif left_occ and not up_occ:
                    clusters[i, j] = clusters[i-1, j]
                elif not left_occ and up_occ:
                    clusters[i, j] = clusters[i, j-1]
                else:
                    # Numbering scheme is row-major
                    lower_cluster, upper_cluster = \
                        (clusters[i-1, j], clusters[i, j-1]) \
                        if (clusters[i-1, j] < clusters[i, j-1]) \
                        else (clusters[i, j-1], clusters[i-1, j])

                    clusters[i, j] = lower_cluster
                    clusters[clusters == upper_cluster
                            ] = lower_cluster
    return clusters


cimport cython
cimport numpy as np
DTYPE=np.int
ctypedef np.int_t DTYPE_t
@cython.boundscheck(False)
@cython.wraparound(False)
def optimized_fun(np.ndarray[DTYPE_t, ndim=2] lattice, int default_val = -1):
    cdef int shape0 = lattice.shape[0]
    cdef int shape1 = lattice.shape[1]

    cdef np.ndarray[DTYPE_t, ndim=2] ids = np.reshape(
        np.arange(
            1, shape0*shape1 + 1
        ),
        (shape0, shape1))

    cdef int i, j, jshift, ishift, x, y    
    cdef DTYPE_t left_occ, up_occ, lower_cluster, upper_cluster
    cdef np.ndarray[DTYPE_t, ndim=2] clusters = default_val*np.ones_like(lattice, dtype = DTYPE)
    for i in range(shape0):
        for j in range(shape1):
            if lattice[i, j]:
                ishift = i-1
                jshift = j-1

                if i == 0:
                    left_occ = 0
                else:
                    left_occ = lattice[ishift, j]

                if j == 0:
                    up_occ = 0
                else:
                    up_occ = lattice[i, jshift]

                if not left_occ and not up_occ:
                    clusters[i, j] = ids[i, j]
                elif left_occ and not up_occ:
                    clusters[i, j] = clusters[ishift, j]
                elif not left_occ and up_occ:
                    clusters[i, j] = clusters[i, jshift]
                else:
                  if (clusters[ishift, j] < clusters[i, jshift]):
                        lower_cluster = clusters[ishift, j]
                        upper_cluster = clusters[i, jshift]
                    else:
                        lower_cluster = clusters[i, jshift]
                        upper_cluster = clusters[ishift, j]

                    clusters[i, j] = lower_cluster
                    #clusters[clusters == upper_cluster
                        #] = lower_cluster
                    # Note that the above line using numpy logic performs substantially faster than the below loop. However, the types of upper_cluster, lower_cluster, and the clusters array all seem to match appropriately. 
                    for x in range(shape0):
                        for y in range(shape1):
                            if clusters[x, y] == upper_cluster:
                                clusters[x, y] = lower_cluster

    return clusters

安装脚本

# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Compiler import Options
import numpy as np

directive_defaults = Options.get_directive_defaults()

directive_defaults['linetrace'] = True
directive_defaults['binding'] = True


extensions = [
    Extension("cython_fail.test_extension", ["cython_fail/test_extension.pyx"], define_macros=[('CYTHON_TRACE', '1')])
]

includes = [
    np.get_include()
]


setup(name = 'cython_fail',
        packages = ['cython_fail'],
        include_dirs = includes,
        ext_modules = cythonize(extensions)
)

分析代码

# filename: profiling.py

import cython_fail
import numpy as np

N = 100
p = 0.58
lattice = (np.random.rand(N, N) < p).astype(np.int)

#import line_profiler

#profile = line_profiler.LineProfiler(cython_fail.test_extension.fun)
#profile.runcall(cython_fail.test_extension.fun, lattice)
#profile.print_stats()
##
#profile = line_profiler.LineProfiler(cython_fail.test_extension.optimized_fun)
#profile.runcall(cython_fail.test_extension.optimized_fun, lattice)
#profile.print_stats()
#quit()
#
#
#
import timeit
original = timeit.timeit("cython_fail.test_extension.fun(lattice)",
            setup="import cython_fail; from __main__ import lattice",
            number=100)
optimized = timeit.timeit("cython_fail.test_extension.optimized_fun(lattice)",
            setup="import cython_fail; from __main__ import lattice",
            number=100)
print("Time to run normally: {}\nTime to run optimized: {}".format(original, optimized))

如果对使用Line Profiler模块的任何人都有帮助,我也会为行分析提供代码。作为参考,上面运行的简单时间的输出是:

Time to run normally:  3.9105381628032774
Time to run optimized: 9.10740948910825

更新

我已将profiling.py脚本移到根目录中(它不是cython_test/test_package/profiling.py而不是cython_test/profiling.py,我现在正在使用这个简单的bash脚本来运行测试

# run_tests.sh
#!/bin/bash
if [ -f ]; then
    rm -rf build
fi
if [ -f cython_fail/test_extension.c ]; then
    rm cython_fail/test_extension.c
fi
if [ -f cython_fail/test_extension.cpython-34m.so ]; then
    rm test_extension.cpython-34m.so
fi
python3 setup.py build_ext --inplace
python3 profiling.py

更新2

我已经确定,如果我将架构从Linux机箱切换到Mac,问题就不会持续存在。在Linux机器上运行逐行分析的结果如下。

Total time: 9.66717 s
File: cython_fail/test_extension.pyx
Function: optimized_fun at line 47

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    47                                           def optimized_fun(np.ndarray[DTYPE_t, ndim=2] lattice, int default_val = -1):
    48         1            3      3.0      0.0      cdef int shape0 = lattice.shape[0]
    49         1            1      1.0      0.0      cdef int shape1 = lattice.shape[1]
    50
    51         2            5      2.5      0.0      cdef np.ndarray[DTYPE_t, ndim=2] ids = np.reshape(
    52         1            1      1.0      0.0          np.arange(
    53         1           51     51.0      0.0              1, shape0*shape1 + 1
    54                                                   ),
    55         1           25     25.0      0.0          (shape0, shape1))
    56
    57                                               cdef int i, j, jshift, ishift, x, y
    58                                               cdef DTYPE_t left_occ, up_occ, lower_cluster, upper_cluster
    59         1          143    143.0      0.0      cdef np.ndarray[DTYPE_t, ndim=2] clusters = default_val*np.ones_like(lattice, dtype = DTYPE)
    60         1            6      6.0      0.0      for i in range(shape0):
    61       100           48      0.5      0.0          for j in range(shape1):
    62     10000         5129      0.5      0.1              if lattice[i, j]:
    63      5818         2718      0.5      0.0                  ishift = i-1
    64      5818         2728      0.5      0.0                  jshift = j-1
    65
    66      5818         2765      0.5      0.0                  if i == 0:
    67        57           28      0.5      0.0                      left_occ = 0
    68                                                           else:
    69      5761         2789      0.5      0.0                      left_occ = lattice[ishift, j]
    70
    71      5818         2762      0.5      0.0                  if j == 0:
    72        60           24      0.4      0.0                      up_occ = 0
    73                                                           else:
    74      5758         2689      0.5      0.0                      up_occ = lattice[i, jshift]
    75
    76      5818         2788      0.5      0.0                  if not left_occ and not up_occ:
    77      1032          582      0.6      0.0                      clusters[i, j] = ids[i, j]
    78      4786         2287      0.5      0.0                  elif left_occ and not up_occ:
    79      1431          692      0.5      0.0                      clusters[i, j] = clusters[ishift, j]
    80      3355         1607      0.5      0.0                  elif not left_occ and up_occ:
    81      1397          674      0.5      0.0                      clusters[i, j] = clusters[i, jshift]
    82                                                           else:
    83
    84      1958          933      0.5      0.0                      if (clusters[ishift, j] < clusters[i, jshift]):
    85       526          236      0.4      0.0                          lower_cluster = clusters[ishift, j]
    86       526          259      0.5      0.0                          upper_cluster = clusters[i, jshift]
    87                                                               else:
    88      1432          688      0.5      0.0                          lower_cluster = clusters[i, jshift]
    89      1432          685      0.5      0.0                          upper_cluster = clusters[ishift, j]
    90
    91      1958          937      0.5      0.0                      clusters[i, j] = lower_cluster
    92                                                               #clusters[clusters == upper_cluster
    93                                                                       #] = lower_cluster
    94      1958          948      0.5      0.0                      for x in range(shape0):
    95    195800        91754      0.5      0.9                          for y in range(shape1):
    96  19580000      9274425      0.5     95.9                              if clusters[x, y] == upper_cluster:
    97    528427       265750      0.5      2.7                                  clusters[x, y] = lower_cluster
    98
    99         1            8      8.0      0.0      return clusters

更新3

这是生成的实际C文件的差异。几个笔记:

  1. 我尝试在两个系统上升级和降级cython版本以匹配,但仍然收到相同的结果。
  2. 在发布之前删除了各种明显无意义的行。
  3. 有一些关于fastcalls或函数的行与关键字看起来可疑,但我不太了解cython或其命名约定,以了解它们生成的代码中的含义。
  4. &lt; line是较慢的版本,&gt;线对应于更快的代码。

    1c1
    < /* Generated by Cython 0.26.1 */
    ---
    > /* Generated by Cython 0.25.2 */
    12,19c12
    <         "depends": [],
    <         "extra_compile_args": [
    <             "-g"
    <         ],
    <         "name": "cython_fail.test_extension",
    <         "sources": [
    <             "cython_fail/test_extension.pyx"
    <         ]
    ---
    >         "depends": []
    32c25
    < #define CYTHON_ABI "0_26_1"
    ---
    > #define CYTHON_ABI "0_25_2"
    54d46
    < #define __PYX_COMMA ,
    72,73d63
    <   #undef CYTHON_USE_PYTYPE_LOOKUP
    <   #define CYTHON_USE_PYTYPE_LOOKUP 0
    101,102d90
    <   #undef CYTHON_USE_PYTYPE_LOOKUP
    <   #define CYTHON_USE_PYTYPE_LOOKUP 0
    134,139d121
    <   #if PY_VERSION_HEX < 0x02070000
    <     #undef CYTHON_USE_PYTYPE_LOOKUP
    <     #define CYTHON_USE_PYTYPE_LOOKUP 0
    <   #elif !defined(CYTHON_USE_PYTYPE_LOOKUP)
    <     #define CYTHON_USE_PYTYPE_LOOKUP 1
    <   #endif
    217,223c199,202
    < #if PY_VERSION_HEX < 0x030700A0 || !defined(METH_FASTCALL)
    <   #ifndef METH_FASTCALL
    <      #define METH_FASTCALL 0x80
    <   #endif
    <   typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject **args, Py_ssize_t nargs);
    <   typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject **args,
    <                                                           Py_ssize_t nargs, PyObject *kwnames);
    ---
    > #ifndef METH_FASTCALL
    >   #define METH_FASTCALL 0x80
    >   typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject **args,
    >                                               Py_ssize_t nargs, PyObject *kwnames);
    226d204
    <   #define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords
    230c208
    <     ((PyCFunction_Check(func) && (METH_FASTCALL == (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_KEYWORDS)))))
    ---
    >     ((PyCFunction_Check(func) && (METH_FASTCALL == (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)))))
    357,362d334
    < #ifndef __has_attribute
    <   #define __has_attribute(x) 0
    < #endif
    < #ifndef __has_cpp_attribute
    <   #define __has_cpp_attribute(x) 0
    < #endif
    417,445d388
    < #ifdef _MSC_VER
    <     #ifndef _MSC_STDINT_H_
    <         #if _MSC_VER < 1300
    <            typedef unsigned char     uint8_t;
    <            typedef unsigned int      uint32_t;
    <         #else
    <            typedef unsigned __int8   uint8_t;
    <            typedef unsigned __int32  uint32_t;
    <         #endif
    <     #endif
    < #else
    <    #include <stdint.h>
    < #endif
    < #ifndef CYTHON_FALLTHROUGH
    <   #ifdef __cplusplus
    <     #if __has_cpp_attribute(fallthrough)
    <       #define CYTHON_FALLTHROUGH [[fallthrough]]
    <     #elif __has_cpp_attribute(clang::fallthrough)
    <       #define CYTHON_FALLTHROUGH [[clang::fallthrough]]
    <     #endif
    <   #endif
    <   #ifndef CYTHON_FALLTHROUGH
    <     #if __has_attribute(fallthrough) || (defined(__GNUC__) && defined(__attribute__))
    <       #define CYTHON_FALLTHROUGH __attribute__((fallthrough))
    <     #else
    <       #define CYTHON_FALLTHROUGH
    <     #endif
    <   #endif
    < #endif
    553,554c496,497
    < static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*);
    < static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
    ---
    > static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*);
    > static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
    567,571c510,511
    < #define __Pyx_PyObject_AsWritableString(s)    ((char*) __Pyx_PyObject_AsString(s))
    < #define __Pyx_PyObject_AsWritableSString(s)    ((signed char*) __Pyx_PyObject_AsString(s))
    < #define __Pyx_PyObject_AsWritableUString(s)    ((unsigned char*) __Pyx_PyObject_AsString(s))
    < #define __Pyx_PyObject_AsSString(s)    ((const signed char*) __Pyx_PyObject_AsString(s))
    < #define __Pyx_PyObject_AsUString(s)    ((const unsigned char*) __Pyx_PyObject_AsString(s))
    ---
    > #define __Pyx_PyObject_AsSString(s)    ((signed char*) __Pyx_PyObject_AsString(s))
    > #define __Pyx_PyObject_AsUString(s)    ((unsigned char*) __Pyx_PyObject_AsString(s))
    693d632
    < static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; }
    698d636
    < static PyObject *__pyx_cython_runtime;
    1268c1206
    <   #define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error)   if ((1)); else goto_error;
    ---
    >   #define __Pyx_TraceCall(funcname, srcfile, firstlineno, nogil, goto_error)   if (1); else goto_error;
    1326c1264
    <   #define __Pyx_TraceLine(lineno, nogil, goto_error)   if ((1)); else goto_error;
    ---
    >   #define __Pyx_TraceLine(lineno, nogil, goto_error)   if (1); else goto_error;
    1451,1453d1388
    < /* IsLittleEndian.proto */
    < static CYTHON_INLINE int __Pyx_Is_Little_Endian(void);
    < 
    1461c1396
    <                               __Pyx_TypeInfo* type);
    ---
    >                               __Pyx_TypeInfo* type); // PROTO
    1580,1582d1514
    < /* CLineInTraceback.proto */
    < static int __Pyx_CLineForTraceback(int c_line);
    < 
    1845d1776
    < static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback";
    1848c1779
    < static const char __pyx_k_cython_fail_test_extension_pyx[] = "cython_fail/test_extension.pyx";
    ---
    1867d1798
    < static PyObject *__pyx_n_s_cline_in_traceback;
    1870d1800
    < static PyObject *__pyx_kp_s_cython_fail_test_extension_pyx;
    6778d6697
    <   {&__pyx_n_s_cline_in_traceback, __pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 0, 1, 1},
    6781d6699
    <   {&__pyx_kp_s_cython_fail_test_extension_pyx, __pyx_k_cython_fail_test_extension_pyx, sizeof(__pyx_k_cython_fail_test_extension_pyx), 0, 0, 1, 0},
    6951c6869
    <   __pyx_codeobj_ = (PyObject*)__Pyx_PyCode_New(3, 0, 11, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__13, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_cython_fail_test_extension_pyx, __pyx_n_s_fun, 4, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj_)) __PYX_ERR(0, 4, __pyx_L1_error)
    ---
    6966c6884
    <   __pyx_codeobj__3 = (PyObject*)__Pyx_PyCode_New(2, 0, 16, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__15, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_cython_fail_test_extension_pyx, __pyx_n_s_optimized_fun, 47, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__3)) __PYX_ERR(0, 47, __pyx_L1_error)
    ---
    7042d6959
    <   __pyx_cython_runtime = PyImport_AddModule((char *) "cython_runtime"); if (unlikely(!__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error)
    7167c7084
    ---
    7184c7101
    <       __Pyx_AddTraceback("init cython_fail.test_extension", 0, __pyx_lineno, __pyx_filename);
    ---
    >       __Pyx_AddTraceback("init cython_fail.test_extension", __pyx_clineno, __pyx_lineno, __pyx_filename);
    7686,7687c7603,7604
    < #endif
    < #endif
    ---
    > #endif  // CPython < 3.6
    > #endif  // CYTHON_FAST_PYCALL
    7695d7611
    <     int flags = PyCFunction_GET_FLAGS(func);
    7697c7613
    <     assert(METH_FASTCALL == (flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_KEYWORDS)));
    ---
    >     assert(METH_FASTCALL == (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)));
    7704,7708c7620
    <     if ((PY_VERSION_HEX < 0x030700A0) || unlikely(flags & METH_KEYWORDS)) {
    <         return (*((__Pyx_PyCFunctionFastWithKeywords)meth)) (self, args, nargs, NULL);
    <     } else {
    <         return (*((__Pyx_PyCFunctionFast)meth)) (self, args, nargs);
    <     }
    ---
    >     return (*((__Pyx_PyCFunctionFast)meth)) (self, args, nargs, NULL);
    7710c7622
    < #endif
    ---
    > #endif  // CYTHON_FAST_PYCCALL
    7769a7682,7684
    > #ifdef __Pyx_CyFunction_USED
    >     if (likely(PyCFunction_Check(func) || PyObject_TypeCheck(func, __pyx_CyFunctionType))) {
    > #else
    7770a7686
    > #endif
    8096c8012
    <     static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
    ---
    >       static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
    8107,8112c8023,8025
    <     Py_ssize_t wrapped_i = i;
    <     if (wraparound & unlikely(i < 0)) {
    <         wrapped_i += PyList_GET_SIZE(o);
    <     }
    <     if ((!boundscheck) || likely((0 <= wrapped_i) & (wrapped_i < PyList_GET_SIZE(o)))) {
    <         PyObject *r = PyList_GET_ITEM(o, wrapped_i);
    ---
    >     if (wraparound & unlikely(i < 0)) i += PyList_GET_SIZE(o);
    >     if ((!boundscheck) || likely((0 <= i) & (i < PyList_GET_SIZE(o)))) {
    >         PyObject *r = PyList_GET_ITEM(o, i);
    8125,8130c8038,8040
    <     Py_ssize_t wrapped_i = i;
    <     if (wraparound & unlikely(i < 0)) {
    <         wrapped_i += PyTuple_GET_SIZE(o);
    <     }
    <     if ((!boundscheck) || likely((0 <= wrapped_i) & (wrapped_i < PyTuple_GET_SIZE(o)))) {
    <         PyObject *r = PyTuple_GET_ITEM(o, wrapped_i);
    ---
    >     if (wraparound & unlikely(i < 0)) i += PyTuple_GET_SIZE(o);
    >     if ((!boundscheck) || likely((0 <= i) & (i < PyTuple_GET_SIZE(o)))) {
    >         PyObject *r = PyTuple_GET_ITEM(o, i);
    8343,8353d8252
    < /* IsLittleEndian */
    <     static CYTHON_INLINE int __Pyx_Is_Little_Endian(void)
    < {
    <   union {
    <     uint32_t u32;
    <     uint8_t u8[4];
    <   } S;
    <   S.u32 = 0x01020304;
    <   return S.u8[0] == 4;
    < }
    < 
    8355c8254,8258
    <     static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx,
    ---
    >       static CYTHON_INLINE int __Pyx_IsLittleEndian(void) {
    >   unsigned int n = 1;
    >   return *(unsigned char*)(&n) != 0;
    > }
    > static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx,
    8741c8644
    <         if (!__Pyx_Is_Little_Endian()) {
    ---
    >         if (!__Pyx_IsLittleEndian()) {
    8750c8653
    <         if (__Pyx_Is_Little_Endian()) {
    ---
    >         if (__Pyx_IsLittleEndian()) {
    8901c8804
    <       static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
    ---
    >         static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
    8914c8817
    <       static void __Pyx_WriteUnraisable(const char *name, CYTHON_UNUSED int clineno,
    ---
    >         static void __Pyx_WriteUnraisable(const char *name, CYTHON_UNUSED int clineno,
    8956c8859
    <       #if CYTHON_FAST_THREAD_STATE
    ---
    >         #if CYTHON_FAST_THREAD_STATE
    8980c8883
    <       #if CYTHON_FAST_THREAD_STATE
    ---
    >         #if CYTHON_FAST_THREAD_STATE
    8990c8893
    <       #if CYTHON_FAST_THREAD_STATE
    ---
    >         #if CYTHON_FAST_THREAD_STATE
    9051c8954
    <         static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) {
    ---
    >           static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) {
    9125c9028
    <         static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) {
    ---
    >           static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) {
    9164c9067
    <         static PyObject *
    ---
    >           static PyObject *
    9746,9781d9648
    < /* CLineInTraceback */
    <             static int __Pyx_CLineForTraceback(int c_line) {
    < #ifdef CYTHON_CLINE_IN_TRACEBACK
    <     return ((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0;
    < #else
    <     PyObject *use_cline;
    < #if CYTHON_COMPILING_IN_CPYTHON
    <     PyObject **cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime);
    <     if (likely(cython_runtime_dict)) {
    <       use_cline = PyDict_GetItem(*cython_runtime_dict, __pyx_n_s_cline_in_traceback);
    <     } else
    < #endif
    <     {
    <       PyObject *ptype, *pvalue, *ptraceback;
    <       PyObject *use_cline_obj;
    <       PyErr_Fetch(&ptype, &pvalue, &ptraceback);
    <       use_cline_obj = __Pyx_PyObject_GetAttrStr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback);
    <       if (use_cline_obj) {
    <         use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True;
    <         Py_DECREF(use_cline_obj);
    <       } else {
    <         use_cline = NULL;
    <       }
    <       PyErr_Restore(ptype, pvalue, ptraceback);
    <     }
    <     if (!use_cline) {
    <         c_line = 0;
    <         PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False);
    <     }
    <     else if (PyObject_Not(use_cline) != 0) {
    <         c_line = 0;
    <     }
    <     return c_line;
    < #endif
    < }
    < 
    9922,9925c9789
    <     if (c_line) {
    <         c_line = __Pyx_CLineForTraceback(c_line);
    <     }
    <     py_code = __pyx_find_code_object(c_line ? -c_line : py_line);
    ---
    >     py_code = __pyx_find_code_object(c_line ? c_line : py_line);
    9930c9794
    <         __pyx_insert_code_object(c_line ? -c_line : py_line, py_code);
    ---
    >         __pyx_insert_code_object(c_line ? c_line : py_line, py_code);
    9960,9962c9824
    <     if ((0)) {}
    <         else if (PyObject_TypeCheck(obj, __pyx_ptype_5numpy_ndarray)) __pyx_pw_5numpy_7ndarray_3__releasebuffer__(obj, view);
    <     view->obj = NULL;
    ---
    >         if (PyObject_TypeCheck(obj, __pyx_ptype_5numpy_ndarray)) { __pyx_pw_5numpy_7ndarray_3__releasebuffer__(obj, view); return; }
    9963a9826
    >     view->obj = NULL;
    9968,9969c9831,9832
    <             /* CIntFromPyVerify */
    <             #define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\
    ---
    >               /* CIntFromPyVerify */
    >               #define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\
    10927,10928d10789
    <         if (PyObject_Hash(*t->p) == -1)
    <             PyErr_Clear();
    10937c10798
    < static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) {
    ---
    > static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) {
    10941c10802
    < static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
    ---
    > static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
    

0 个答案:

没有答案