Незаконный доступ к памяти на cudaDeviceSynchronize

Я столкнулся с очень странной ошибкой, заключающейся в том, что я получаю ошибку «незаконный доступ к памяти» при запуске симуляции Heat 2D определенного размера, но симуляция работает хорошо, если я запускаю точно такую ​​же симуляцию, только с меньшим количеством элементов.

Есть ли причина, по которой увеличение размера массива вызовет это исключение? Я использую графический процессор Titan Black (6 ГБ памяти), но симуляция, которую я запускаю, и близко не соответствует этому размеру. Я подсчитал, что могу запустить симуляцию 4000 x 4000, но получаю ошибки, если превышаю 250 x 250.

Ошибка возникает сразу после того, как я создаю массив объектов моделирования на устройстве. Код инстанцирования выглядит следующим образом:

template<typename PlaceType, typename StateType>
__global__ void instantiatePlacesKernel(Place** places, StateType *state,
        void *arg, int *dims, int nDims, int qty) {
    unsigned idx = blockDim.x * blockIdx.x + threadIdx.x;

    if (idx < qty) {
        // set pointer to corresponding state object
        places[idx] = new PlaceType(&(state[idx]), arg);
        places[idx]->setIndex(idx);
        places[idx]->setSize(dims, nDims);
    }
}

template<typename PlaceType, typename StateType>
Place** DeviceConfig::instantiatePlaces(int handle, void *argument, int argSize,
        int dimensions, int size[], int qty) {

    // add global constants to the GPU
    memcpy(glob.globalDims,size, sizeof(int) * dimensions);
    updateConstants(glob);

    // create places tracking
    PlaceArray p; // a struct to track qty, 
    p.qty = qty;

    // create state array on device
    StateType* d_state = NULL;
    int Sbytes = sizeof(StateType);
    CATCH(cudaMalloc((void** ) &d_state, qty * Sbytes));
    p.devState = d_state; // save device pointer

    // allocate device pointers
    Place** tmpPlaces = NULL;
    int ptrbytes = sizeof(Place*);
    CATCH(cudaMalloc((void** ) &tmpPlaces, qty * ptrbytes));
    p.devPtr = tmpPlaces; // save device pointer

    // handle arg if necessary
    void *d_arg = NULL;
    if (NULL != argument) {
        CATCH(cudaMalloc((void** ) &d_arg, argSize));
        CATCH(cudaMemcpy(d_arg, argument, argSize, H2D));
    }

    // load places dimensions
    int *d_dims;
    int dimBytes = sizeof(int) * dimensions;
    CATCH(cudaMalloc((void** ) &d_dims, dimBytes));
    CATCH(cudaMemcpy(d_dims, size, dimBytes, H2D));

    // launch instantiation kernel
    int blockDim = (qty - 1) / BLOCK_SIZE + 1;
    int threadDim = (qty - 1) / blockDim + 1;
    Logger::debug("Launching instantiation kernel");
    instantiatePlacesKernel<PlaceType, StateType> <<<blockDim, threadDim>>>(tmpPlaces, d_state,
            d_arg, d_dims, dimensions, qty);
    CHECK();

    CATCH(cudaDeviceSynchronize()); // ERROR OCCURS HERE

    // clean up memory
    if (NULL != argument) {
        CATCH(cudaFree(d_arg));
    }
    CATCH(cudaFree(d_dims));
    CATCH(cudaMemGetInfo(&freeMem, &allMem));

    return p.devPtr;
}

Пожалуйста, предполагайте, что все пользовательские типы, которые вы видите, работают, так как этот код выполняется без ошибок при достаточно небольшом моделировании. Я разочарован тем, что количество элементов в местах функций ядра и массивах состояний вызывает ошибку, когда размер превышает 250 x 250 элементов. Любое понимание было бы потрясающим.

Благодарю вас!


person Acerebral    schedule 03.02.2015    source источник
comment
Обратите внимание, что для подобных вопросов (почему этот код не работает?) SO ожидает, что вы предоставите полный MCVE. Это не совсем обязательно, согласно ожиданиям SO. Непредоставление MCVE является веской причиной для закрытия такого вопроса.   -  person Robert Crovella    schedule 03.02.2015


Ответы (1)


Я думаю, что, вероятно, происходит сбой в ядре new, потому что вы выделяете слишком много памяти.

В ядре new поведение и ограничения аналогичны в ядре malloc. Эти выделения ограничены кучей устройства, которая по умолчанию начинается с 8 МБ. Если размер массива 250x250 соответствует чему-то в этом диапазоне (8 МБ), то существенное превышение этого значения приведет к тому, что некоторые из новых операций будут «молча» завершаться ошибкой (т. е. возвращать нулевые указатели). Если вы затем попытаетесь использовать эти нулевые указатели, вы получите незаконный доступ к памяти.

Несколько рекомендаций:

  1. Выясните, сколько места вам нужно, и заранее зарезервируйте его в куче устройства, используя cudaDeviceSetLimit(cudaLimitMallocHeapSize, size_t size)
  2. Когда у вас возникают проблемы с ядрами, которые используют new или malloc, может быть полезно для целей отладки, возможно, использовать макрос отладки для проверки возвращаемых указателей на NULL. В целом это хорошая практика.
  3. Вы можете узнать, как отлаживать незаконный доступ к памяти с большей ясностью (локализировать его до определенной строки в конкретном ядре), используя метод, описанный здесь.
person Robert Crovella    schedule 03.02.2015