Проблемы с размещением объектов в Cuda - PullRequest
0 голосов
/ 13 мая 2018

В настоящее время я делаю кое-что на Cuda C ++ для глубокого обучения (техника Карандаш и бумага), но застрял на странном поведении Cuda.

Вот мой класс:

class Matrix
{
public:
    float* data;
    int width;
    int height;

    Matrix();
    Matrix(const Matrix&);
    ~Matrix();
    void reset();
    friend std::ostream& operator<<(std::ostream&, const Matrix*);
};

Его определение:

Matrix::Matrix()
{
}

Matrix::Matrix(const Matrix& copy) : width(copy.width), height(copy.height)
{
    data = new float[width * height];
    std::copy(copy.data, copy.data + width * height, data);
}

Matrix::~Matrix()
{
    delete data;
}

void Matrix::reset()
{
    memset(data, 0, width * height * sizeof(float));
}

std::ostream& operator<<(std::ostream& out, const Matrix* matrix)
{   
    for (int i = 1; i <= matrix->height * matrix->width; ++i)
        out << matrix->data[i - 1] << (i % matrix->width == 0 ? "\n" : "\t");
    return out;
}

А вот минимальный, полный и проверяемый пример:

__global__ void add_and_display(Matrix* dev_weights)
{
    dev_weights->data[blockIdx.x * dev_weights->width + threadIdx.x] += 1.f;
}

int main()
{
    Matrix* weights = new Matrix(), *dev_weights;
    float* weights_elements;

    //For the purpose of testing, creating a checked pattern Matrix
    weights->width = 9;
    weights->height = 9;
    weights->data = new float[weights->width * weights->height];
    for (int i = 0; i < weights->width * weights->height; ++i)
    {
        if (i % 2 == 0) 
            weights->data[i] = 0;
        else 
            weights->data[i] = 1;
    }

    int weights_size = weights->width * weights->height * sizeof(float);

    HANDLE_ERROR(cudaMalloc((void **)&weights_elements, weights_size));

    //Allocate objects on the device
    HANDLE_ERROR(cudaMalloc((void **)&dev_weights, sizeof(Matrix)));

    //Copy the data to the object allocated on the device
    HANDLE_ERROR(cudaMemcpy(dev_weights, weights, sizeof(Matrix), cudaMemcpyHostToDevice));
    HANDLE_ERROR(cudaMemcpy(weights_elements, weights->data, weights_size, cudaMemcpyHostToDevice));
    HANDLE_ERROR(cudaMemcpy(&(dev_weights->data), &weights_elements, sizeof(float*), cudaMemcpyHostToDevice));

    add_and_display <<< weights->width, weights->height >>> (dev_weights);

    HANDLE_ERROR(cudaDeviceSynchronize());

    //Copy back data from device
    float* hostPointer = new float[weights->width * weights->height];
    HANDLE_ERROR(cudaMemcpy(weights, dev_weights, sizeof(Matrix), cudaMemcpyDeviceToHost));
    HANDLE_ERROR(cudaMemcpy(hostPointer, weights->data, weights_size, cudaMemcpyDeviceToHost));

    //Display and get errors here
    cout << weights << endl;

    cudaFree(dev_weights);

    return 0;
}

Вот мой макрос для проверки ошибок:

static void HandleError(cudaError_t err, const char *file, int line) {
    if (err != cudaSuccess) {
        printf("%s in %s at line %d\n", cudaGetErrorString(err), file, line);
        exit(EXIT_FAILURE);
    }
}
#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))

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

Спасибо, если вы можете помочь мне найти проблему.


РЕДАКТИРОВАТЬ 1: Упростил мой пост, чтобы его можно было проверить всем.

1 Ответ

0 голосов
/ 14 мая 2018

На самом деле нет ничего плохого в распределении, передаче и использовании объектов на устройстве.Существует одна небольшая ошибка в передаче данных обратно с устройства в конце MCVE, который является источником segfaults.Это:

//Copy back data from device
float* hostPointer = new float[weights->width * weights->height];
HANDLE_ERROR(cudaMemcpy(weights, dev_weights, sizeof(Matrix), cudaMemcpyDeviceToHost));
HANDLE_ERROR(cudaMemcpy(hostPointer, weights->data, weights_size, cudaMemcpyDeviceToHost));

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

//Copy back data from device
float* hostPointer = new float[weights->width * weights->height];
HANDLE_ERROR(cudaMemcpy(weights, dev_weights, sizeof(Matrix), cudaMemcpyDeviceToHost));
HANDLE_ERROR(cudaMemcpy(hostPointer, weights->data, weights_size, cudaMemcpyDeviceToHost));
weights->data = hostPointer; // weights data must point to hostPointer

Тогда код работает правильно:

$ cat weights.cu
#include <iostream>
#include <cstdio>

static void HandleError(cudaError_t err, const char *file, int line) {
    if (err != cudaSuccess) {
        printf("%s in %s at line %d\n", cudaGetErrorString(err), file, line);
        exit(EXIT_FAILURE);
    }
}

#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))
class Matrix
{
public:
    float* data;
    int width;
    int height;

    Matrix();
    Matrix(const Matrix&);
    ~Matrix();
    void reset();
    friend std::ostream& operator<<(std::ostream&, const Matrix*);
};

Matrix::Matrix()
{
}

Matrix::Matrix(const Matrix& copy) : width(copy.width), height(copy.height)
{
    data = new float[width * height];
    std::copy(copy.data, copy.data + width * height, data);
}

Matrix::~Matrix()
{
    delete data;
}

void Matrix::reset()
{
    memset(data, 0, width * height * sizeof(float));
}

std::ostream& operator<<(std::ostream& out, const Matrix* matrix)
{   
    for (int i = 1; i <= matrix->height * matrix->width; ++i)
        out << matrix->data[i - 1] << (i % matrix->width == 0 ? "\n" : "\t");
    return out;
}

__global__ void add_and_display(Matrix* dev_weights)
{
    dev_weights->data[blockIdx.x * dev_weights->width + threadIdx.x] += 1.f;
}

int main()
{
    Matrix* weights = new Matrix(), *dev_weights;
    float* weights_elements;

    //For the purpose of testing, creating a checked pattern Matrix
    weights->width = 9;
    weights->height = 9;
    weights->data = new float[weights->width * weights->height];
    for (int i = 0; i < weights->width * weights->height; ++i)
    {
        if (i % 2 == 0) 
            weights->data[i] = 0;
        else 
            weights->data[i] = 1;
    }

    int weights_size = weights->width * weights->height * sizeof(float);

    HANDLE_ERROR(cudaMalloc((void **)&weights_elements, weights_size));

    //Allocate objects on the device
    HANDLE_ERROR(cudaMalloc((void **)&dev_weights, sizeof(Matrix)));

    //Copy the data to the object allocated on the device
    HANDLE_ERROR(cudaMemcpy(dev_weights, weights, sizeof(Matrix), cudaMemcpyHostToDevice));
    HANDLE_ERROR(cudaMemcpy(weights_elements, weights->data, weights_size, cudaMemcpyHostToDevice));
    HANDLE_ERROR(cudaMemcpy(&(dev_weights->data), &weights_elements, sizeof(float*), cudaMemcpyHostToDevice));

    add_and_display <<< weights->width, weights->height >>> (dev_weights);

    HANDLE_ERROR(cudaDeviceSynchronize());

    //Copy back data from device
    float* hostPointer = new float[weights->width * weights->height];
    HANDLE_ERROR(cudaMemcpy(weights, dev_weights, sizeof(Matrix), cudaMemcpyDeviceToHost));
    HANDLE_ERROR(cudaMemcpy(hostPointer, weights->data, weights_size, cudaMemcpyDeviceToHost));
    weights->data = hostPointer;

    //Display and get errors here
    std::cout << weights << std::endl;

    cudaFree(dev_weights);

    return 0;
}

$ nvcc -g -arch=sm_52 -o weights weights.cu 

$ cuda-memcheck weights
========= CUDA-MEMCHECK
1   2   1   2   1   2   1   2   1
2   1   2   1   2   1   2   1   2
1   2   1   2   1   2   1   2   1
2   1   2   1   2   1   2   1   2
1   2   1   2   1   2   1   2   1
2   1   2   1   2   1   2   1   2
1   2   1   2   1   2   1   2   1
2   1   2   1   2   1   2   1   2
1   2   1   2   1   2   1   2   1

========= ERROR SUMMARY: 0 errors
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...