Индексирование потоков вне границ в ядре CUDA - PullRequest
0 голосов
/ 07 мая 2019

Я использую ядро ​​CUDA, которое, кажется, индексирует вне границ, и я не могу понять, почему. Я получаю ошибку 8 записи размера в cuda-memcheck.

Я попытался изменить количество блоков и количество потоков в каждом блоке, а также выполнить только часть всех необходимых итераций. Вот некоторая полезная информация, а также воспроизводимый пример, который выдает ошибку:

Размер блока: 128

numBlocks: 512

Nvidia GTX 970

#include <iostream>
#include <cuda_runtime_api.h>
#include <cuda.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <vector>
#include <iterator>
#include <cuda_profiler_api.h>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <stdio.h> 
#include <fstream>



__host__ 
int NchooseK(const int &N, const int &K)
{
    int result = 1;
    for (int i = 1; i <= K; i++)
    {
        result *= N - (K - i);
        result /= i;
    }
    return result;
}


__host__
inline int get_flatten_size(const unsigned int N){
    int sum = 0;
    for(int i=1; i<=N ; i++){
        sum +=i*NchooseK(N,i);
    }
    return sum;
}


__host__
std::vector<int> comb(const int &N, const int &K, const int &length)
//void comb(int N, int K, int length)
{
    int k;
    std::vector<int> vec(K);
    std::vector<int> flatten_vec(0);
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's

    for (int j = 0; j < length; j++) {
        k = 0;
        for (int i = 0; i < N; ++i) // [0..N-1] integers
        {
            if (bitmask[i]) {
                //std::cout << i << " ";
                vec[k] = i;
                k++;
            }
            //std::cout << std::endl;
        }
        std::prev_permutation(bitmask.begin(), bitmask.end());
        flatten_vec.insert(flatten_vec.end(), vec.begin(),vec.end());
    }
    return flatten_vec;
}

__host__
void get_matrix_indices(const unsigned int N, int *sub_col, int *sub_size, int *cumulative_size)
{
    int size, itterator = 0;    
    cumulative_size[0] = 0;
    std::vector<int> size_i_columns;
    std::vector<int> all_columns(0);

    for(int i=1; i<=N; i++){
        size = NchooseK(N,i);
        size_i_columns = comb(N,i,size);
        for(int j=0; j<size; j++){
            sub_size[itterator]=i;
            cumulative_size[itterator+1]=cumulative_size[itterator]+i;
            itterator++; 
        }
        all_columns.insert(all_columns.end(),size_i_columns.begin(),size_i_columns.end());
    }   
    //sub_col = &all_columns[0];
    for(int i = 0; i < all_columns.size(); i++) sub_col[i] = all_columns[i];
}



__global__
void comb_ols(const unsigned int M, const unsigned int N, int* sub_col, int *sub_size, int* cumulative_size, const unsigned int numberOfCalculations, const unsigned int max_size){

    int size;   
    int start_index;

    int index = blockIdx.x*blockDim.x+threadIdx.x;
    int stride = blockDim.x*gridDim.x;

    double *sub_matrix = new double[M*(1+max_size)];


    for(int i = index; i < numberOfCalculations; i+=stride){    


        size = sub_size[i];
        start_index = cumulative_size[i];             



            for(int j = 0; j < size; j++){
            for(int k  = 0; k<M; k++){

                sub_matrix[k] = 1;


                                        }       
            }
        }


    delete [] sub_matrix;


}

А то у нас основная функция:

int main() 
{   

    int N = 17;
    int M = 263;

    const unsigned int regressors = N-1;
    const unsigned int numberOfCalculations = (int) (exp2((double) regressors) - 1);
    const unsigned int size_sub_col = get_flatten_size(regressors);

    int blockSize =128;
    int numBlocks = (numberOfCalculations + blockSize-1)/blockSize;

    std::cout << "\nblockSize :" << blockSize;      
    std::cout << "\nnumBlocks :" << numBlocks;      
    std::cout << "\nblockSize*numBlocks :" << blockSize*numBlocks;      


    std::cout << "\nregressors :" << regressors;        
    std::cout << "\nNumberOfCalculations :" << numberOfCalculations;        
    std::cout << "\nsize_sub_col :" << size_sub_col << '\n' ;       


    int *sub_size, *cumulative_size, *sub_columns;

    cudaMallocManaged(&sub_size, numberOfCalculations*sizeof(int));
    cudaMallocManaged(&cumulative_size, (numberOfCalculations+1)*sizeof(int));
    cudaMallocManaged(&sub_columns, size_sub_col*sizeof(int));

    get_matrix_indices(regressors,sub_columns, sub_size, cumulative_size);

    const unsigned int max_size = N*M;







    cudaProfilerStart();
    comb_ols<<<numBlocks, blockSize>>>(M,N,sub_columns, sub_size, cumulative_size, numberOfCalculations, max_size);
    cudaProfilerStop();


    cudaDeviceSynchronize();








    cudaFree(sub_size);
    cudaFree(cumulative_size);
    cudaFree(sub_columns);

    return 0;
}

Я не понимаю, почему потоки пытаются получить доступ к недопустимому пространству памяти. Насколько я понял, матрица sub_matrix будет инициализироваться в каждом потоке один раз, а затем происходит параллельный цикл for. Таким образом, каждый поток должен иметь необходимое пространство памяти. Я выделяю слишком много памяти на GPU? Как здесь обрабатывается «новая sub_matrix»?

1 Ответ

2 голосов
/ 07 мая 2019

Если я правильно прочитал ваш код, каждый поток пытается выделить M * (1 + M*N) double, что составляет 263 * (1 + 263 * 17) = 1 176 136 double или 8,97 МБ памяти кучи на поток.Вы запускаете 128 * 512 потоков.Это означало бы, что для успешной работы ядра вам потребуется 588 ГБ пространства кучи.

Очевидно, что вашему графическому процессору не хватает этого объема памяти, и доступ к памяти за пределами границ происходит из-за сбоев в вызове new (который вы можете проверить, кстати).

Могу ли я предположить, чточто-то в вычислениях размера для кучи памяти, которое вам требуется, неверноВ противном случае у вас возникнет крайне нереалистичная проблема с графическим процессором, и вам потребуется другой подход.

Обратите внимание, что даже если вам удастся изменить дизайн, чтобы ограничить код допустимым объемом памяти кучи памяти, вам все равно потребуетсяСкорее всего, перед запуском ядра измените размер кучи malloc до подходящего размера.Для этого можно использовать API cudaDeviceSetLimit.

...