简体   繁体   中英

cython prange - dealing with __Pyx_ErrFetchWithState / __Pyx_ErrRestoreWithState

I am using prange in order to modify an array. I try to have as few yellow lines as possible in the HTML page generated by the cython compiler with:

cython function_prange.pyx -a

But when extracting a part of an array to modify it, I always have this kind of code which is generated:

{
    #ifdef WITH_THREAD
    PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
    #endif
    #ifdef _OPENMP
    #pragma omp flush(__pyx_parallel_exc_type)
    #endif /* _OPENMP */
    if (!__pyx_parallel_exc_type) {
      __Pyx_ErrFetchWithState(&__pyx_parallel_exc_type, &__pyx_parallel_exc_value, &__pyx_parallel_exc_tb);
      __pyx_parallel_filename = __pyx_filename; __pyx_parallel_lineno = __pyx_lineno; __pyx_parallel_clineno = __pyx_clineno;
      __Pyx_GOTREF(__pyx_parallel_exc_type);
    }
    #ifdef WITH_THREAD
    PyGILState_Release(__pyx_gilstate_save);
    #endif
}

Is it possible to avoid these __Pyx_ErrFetchWithState / __Pyx_ErrRestoreWithState ? Does it really matter?

Here is the code that I am using:

A first function adding a double to the diagonal of a matrix contained in an array of size one in function_nogil.pyx

import cython

@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False)  # turn off negative index wrapping for entire function
cdef void add_diag(double [:,:,:] a, int a_len, int a_wid, double coeff) nogil:
    cdef int x_max = a_len

    cdef int x
    for x in xrange(x_max):
        a[0,x,x] = a[0,x,x] + coeff

Its header in function_nogil.pxd

cdef void add_diag(double [:,:,:] a, int a_len, int a_wid, double coeff) nogil

The function using prange in function_prange.pyx

@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False)  # turn off negative index wrapping for entire function
def prange_loop_idx(double [:,:,:] a, int a_dim1, int a_dim2, int a_dim3, double coeff,int num_threads):

    cdef int i = 0

    with nogil,parallel(num_threads=num_threads):
        for i in prange(a_dim1):
            add_diag(a[i:i+1:,],a_dim2,a_dim3,coeff)

It's not as bad as it looks. When you look at the full C code you'll see that this is just an error handling bit of code and is only be executed by jumping to it with goto following an error condition.

(The error condition is that it checks that the memoryview slice is constructed correctly - I think this is a consequence of using a slice rather than a single index. Things like setting the step to 0 will trigger an error here).

So you have a little bit of extra error checking that probably isn't strictly necessary but I think would be wise to leave alone (on my PC it's this line):

if (unlikely(__pyx_memoryview_slice_memviewslice(
   &__pyx_t_4,
    __pyx_v_a.shape[0], __pyx_v_a.strides[0], __pyx_v_a.suboffsets[0],
    0,
    0,
    &__pyx_t_5,
    __pyx_v_i,
    (__pyx_v_i + 1),
    0,
    1,
    1,
    0,
    1) < 0))
{
    __PYX_ERR(0, 21, __pyx_L12_error)
}

but the actual code you're worried about is not called in normal operation, so has almost no cost and you can ignore.


I think the code generated by parallel and prange is probably sufficiently complex that the generated html files aren't 100% useful at showing you the correct bits, and this is why you're seeing it linked to that line, but you're not seeing the error checking code I've shown above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM