Slowing down numpy code with cython

Viewed 335

I'm trying to accelerate an algorithm I have with cython. I've followed most of the proscriptions on this page to take my original function and convert it into an optimized form, but all I appear to have done is slowed things down. I've checked the types pretty thoroughly, even looking into the generated C code, and I've done some line profiling as well that suggests that a lot of time is spent just comparing LONG types, but aside from that I haven't had any luck. I've noted inline the loop that seems to be the most expensive.

Am I just running into some very tight edge case? I find it hard to believe that cython wouldn't be able to optimize this code, so I assume that somewhere something is getting typed wrong.

My testing directory looks like this

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

And the individual files looks like this:

Init script

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

Core code

# 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 script

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

Profiling code

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

I've included code for line profiling as well if that is helpful to anyone using the Line Profiler module. For reference, the output of the simple timeit runs above is:

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

Update

I've moved the profiling.py script into the root (it is not cython_test/test_package/profiling.py instead of just cython_test/profiling.py, and I am now using this simple bash script to run the tests

# 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

Update 2

I've established that the problem does not persist if I switch architectures from a Linux box to Mac. The result of running line-by-line profiling on the Linux box is below.

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

Update 3

This is the diff of the actual C files generated. A couple notes:

  1. I've tried both upgrading and downgrading cython versions on both systems to match, and still received the same results.
  2. Various obviously meaningless lines were removed before posting.
  3. There are some lines to do with fastcalls or functions with keywords that look suspicious, but I don't know nearly enough about cython or its naming conventions to know what those mean in the code it generates.

The < lines are the slower version, and the > lines correspond to the faster code.

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 Answers
Related