Magnus Wang
Magnus Wang

Reputation: 180

Cython crashed using memoryviews

When removing the gil and using a memory view, this error appears:

Fatal Python error: PyThreadState_Get: no current thread

The same object has worked fine as a memory view before. See code below for more details.

# @boundscheck(False)
# @wraparound(False)
# @nonecheck(False)
cpdef c_calculated_mutation(
        np.ndarray[int, ndim=3] population,
        long mutations,
        long[:] depotmap,
        double[:,:] mapping,
        np.ndarray[long, ndim=1] loads,
        dict max_loads,
        np.ndarray[long, ndim=1] durations,
        dict max_durations
):
    cdef:
        int[:,:,:] pop = population
        int xLen = len(population[0][0])
        int yLen = len(population[0])
        int zLen = len(population)
        list export_changes = []
        int x, y, z, xx, yy, i, max_depot, prev, value, mutation
        float new_fitness, best
        int[:,:] view
        int* load_limits     = <int*>malloc(len(max_loads) * sizeof(int))
        int* duration_limits = <int*>malloc(len(max_durations) * sizeof(int))
        int* fxes = <int*>calloc(mutations, sizeof(int))
        int* fyes = <int*>calloc(mutations, sizeof(int))
        int* txes = <int*>calloc(mutations, sizeof(int))
        int* tyes = <int*>calloc(mutations, sizeof(int))
        int* zes  = <int*>calloc(mutations, sizeof(int))
        int* xxx = <int*>calloc(mutations, sizeof(int))
        int* yyy = <int*>calloc(mutations, sizeof(int))
        int* zzz  = <int*>calloc(mutations, sizeof(int))

    i=0
    for value in max_loads:
        load_limits[i] = int(max_loads[value])
        duration_limits[i] = int(max_durations[value])
        max_depot = value
        i += 1

    for mutation in prange(mutations, nogil=True):
        z = rand() % zLen
        y = rand() % yLen
        x = rand() % xLen
        value = population[z, y, x]
        while value >= 0:
            x = rand() % xLen
            value = population[z, y, x]
        xxx[mutation] = x
        yyy[mutation] = y
        zzz[mutation] = z

That was the setup. The bug comes from the lines with the commented out # with gil. If i use the gil, the program slows down alot. Also, using the same object as a memory view in another function works perfectly fine. I do not get this.

    for mutation in prange(mutations, nogil=True, num_threads=8):
        x = xxx[mutation]
        y = yyy[mutation]
        z = zzz[mutation]

        value = population[z, y, x]
        xx = x
        # with gil:
        best = fitness(pop[z,:,:], xLen, yLen, depotmap, max_depot, mapping, loads, load_limits, durations, duration_limits)
        for yy in range(yLen):
            prev = population[z, yy, xx]
            population[z, yy, xx] = value
            population[z, y,  x ] = prev
            # with gil:
            new_fitness = fitness(pop[z, :, :], xLen, yLen, depotmap, max_depot, mapping, loads, load_limits, durations, duration_limits)
            if best > new_fitness:
                best = new_fitness
                fxes[mutation] = x
                fyes[mutation]  = y
                txes[mutation]  = xx
                tyes[mutation] = yy
                zes[mutation] = z

            population[z, y,  x ] = value
            population[z, yy, xx] = prev

Then the rest of the function.

    for mutation in range(mutations):
        x  = fxes[mutation]
        y  = fyes[mutation]
        xx = txes[mutation]
        yy = tyes[mutation]
        z  = zes[mutation]
        export_changes += [z]
        prev = population[z, yy, xx]
        population[z, yy, xx] = population[z, y, x]
        population[z, y,  x ] = prev

    free(load_limits)
    free(duration_limits)
    free(fxes)
    free(fyes)
    free(txes)
    free(tyes)
    free(zes)
    free(xxx)
    free(yyy)
    free(zzz)

    return population, export_changes

Upvotes: 4

Views: 363

Answers (1)

DavidW
DavidW

Reputation: 30887

loads and durations are typed as ndarray rather than as memoryviews. When you pass them to the function this will require reference counting and so won't work. Unfortunately Cython doesn't diagnose it at compile-time so it just crashes. Change them to memoryviews in c_calculated_mutation to fix the problem. Here's a brief example to demonstrate:

cimport numpy as np

# won't allow me to add "nogil" at Cython compile stage
cdef int f(np.ndarray[np.int32_t,ndim=1] x):
    return 1

cdef int g(np.int32_t[:] x) nogil:
    return 2

def call_funcs(np.ndarray[np.int32_t,ndim=1] a, np.int32_t[:] b):
    # can't do "with nogil:" - needs gil to call
    f(a)
    with nogil:
        g(b) # fine
    print("g(b) done")
    with nogil:
        g(a) # crashes

This gives the output:

g(b) done

Fatal Python error: PyThreadState_Get: no current thread

Current thread 0x00007f3233184540 (most recent call first):

File "<stdin>", line 1 in <module>

Aborted (core dumped)


If that doesn't fix it there may also be something inside fitness since we can't see its contents. It either needs to be a nogil cdef function (i.e. cdef int fitness(...) nogil:) or it needs to be a C function.

Upvotes: 3

Related Questions