Почему операции с массивом портят значения? - PullRequest
0 голосов
/ 11 февраля 2012

Я пытаюсь реализовать Оптимизацию роя частиц на CUDA. Я частично инициализирую массивы данных на хосте, затем выделяю память на CUDA и копирую ее туда, а затем пытаюсь продолжить инициализацию.

Проблема в том, что когда я пытаюсь изменить элемент массива следующим образом

__global__ void kernelInit(
    float* X, 
    size_t pitch, 
    int width, 
    float X_high, 
    float X_low
) {
    // Silly, but pretty reliable way to address array elements
    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int r = tid / width;
    int c = tid % width;
    float* pElement = (float*)((char*)X + r * pitch) + c;
    *pElement = *pElement * (X_high - X_low) - X_low;
    //*pElement = (X_high - X_low) - X_low;
}

Это искажает значения и дает мне 1.#INF00 как элемент массива. Когда я раскомментирую последнюю строку *pElement = (X_high - X_low) - X_low; и прокомментирую предыдущую, она работает, как и ожидалось: я получаю значения типа 15.36 и т. Д.

Я полагаю, что проблема связана либо с моим распределением памяти и копированием, и / или с обращением к конкретному элементу массива. Я прочитал руководство CUDA по этим обеим темам, но не могу обнаружить ошибку: я все еще получаю поврежденный массив, если я делаю что-нибудь с элементом массива. Например, *pElement = *pElement * 2 дает необоснованно большие результаты, такие как 779616...00000000.00000, когда ожидается, что начальный pElement будет просто плавающей точкой в ​​[0;1].

Вот полный источник. Инициализация массивов начинается в main (внизу исходного кода), затем функция f1 выполняет работу для CUDA и запускает ядро ​​инициализации kernelInit:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <cuda.h>
#include <cuda_runtime.h>

const unsigned f_n = 3;
const unsigned n = 2;
const unsigned p = 64;

typedef struct {
    unsigned k_max;
    float c1;
    float c2;
    unsigned p;
    float inertia_factor;
    float Ef;
    float X_low[f_n];
    float X_high[f_n];
    float X_min[n][f_n];
} params_t;

typedef void (*kernelWrapperType) (
    float *X, 
    float *X_highVec, 
    float *V, 
    float *X_best, 
    float *Y, 
    float *Y_best, 
    float *X_swarmBest, 
    bool &termination, 
    const float &inertia, 
    const params_t *params,
    const unsigned &f
);

typedef float (*twoArgsFuncType) (
    float x1, 
    float x2
);

__global__ void kernelInit(
    float* X, 
    size_t pitch, 
    int width, 
    float X_high, 
    float X_low
) {
    // Silly, but pretty reliable way to address array elements
    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int r = tid / width;
    int c = tid % width;
    float* pElement = (float*)((char*)X + r * pitch) + c;
    *pElement = *pElement * (X_high - X_low) - X_low;
    //*pElement = (X_high - X_low) - X_low;
}

__device__ float kernelF1(
    float x1, 
    float x2
) {
    float y = pow(x1, 2.f) + pow(x2, 2.f);
    return y;
}

void f1(
    float *X, 
    float *X_highVec, 
    float *V, 
    float *X_best, 
    float *Y, 
    float *Y_best, 
    float *X_swarmBest, 
    bool &termination, 
    const float &inertia, 
    const params_t *params,
    const unsigned &f
) {
    float *X_d = NULL;
    float *Y_d = NULL;
    unsigned length = n * p;
    const cudaChannelFormatDesc desc = cudaCreateChannelDesc<float4>();
    size_t pitch;
    size_t dpitch;
    cudaError_t err;
    unsigned width = n;
    unsigned height = p;

    err = cudaMallocPitch (&X_d, &dpitch, width * sizeof(float), height);
    pitch = n * sizeof(float);
    err = cudaMemcpy2D(X_d, dpitch, X, pitch, width * sizeof(float), height, cudaMemcpyHostToDevice);

    err = cudaMalloc (&Y_d, sizeof(float) * p);
    err = cudaMemcpy (Y_d, Y, sizeof(float) * p, cudaMemcpyHostToDevice);

    dim3 threads; threads.x = 32;
    dim3 blocks; blocks.x = (length/threads.x) + 1;

    kernelInit<<<threads,blocks>>>(X_d, dpitch, width, params->X_high[f], params->X_low[f]);

    err = cudaMemcpy2D(X, pitch, X_d, dpitch, n*sizeof(float), p, cudaMemcpyDeviceToHost);
    err = cudaFree(X_d);

    err = cudaMemcpy(Y, Y_d, sizeof(float) * p, cudaMemcpyDeviceToHost);
    err = cudaFree(Y_d);
}

float F1(
    float x1, 
    float x2
) {
    float y = pow(x1, 2.f) + pow(x2, 2.f);
    return y;
}

/*
 * Generates random float in [0.0; 1.0]
 */
float frand(){
    return (float)rand()/(float)RAND_MAX;
}

/*
 * This is the main routine which declares and initializes the integer vector, moves it to the device, launches kernel
 * brings the result vector back to host and dumps it on the console.
 */
int main() {
    const params_t params = {
        100, 
        0.5,
        0.5,
        p,
        0.98,
        0.01,
        {-5.12, -2.048, -5.12},
        {5.12, 2.048, 5.12},
        {{0, 1, 0}, {0, 1, 0}}
    };
    float X[p][n];
    float X_highVec[n];
    float V[p][n];
    float X_best[p][n];
    float Y[p] = {0};
    float Y_best[p] = {0};
    float X_swarmBest[n];

    kernelWrapperType F_wrapper[f_n] = {&f1, &f1, &f1};
    twoArgsFuncType F[f_n] = {&F1, &F1, &F1};

    for (unsigned f = 0; f < f_n; f++) {
        printf("Optimizing function #%u\n", f);

        srand ( time(NULL) );
        for (unsigned i = 0; i < p; i++)
            for (unsigned j = 0; j < n; j++)
                X[i][j] = X_best[i][j] = frand();
        for (int i = 0; i < n; i++)
            X_highVec[i] = params.X_high[f];
        for (unsigned i = 0; i < p; i++)
            for (unsigned j = 0; j < n; j++)
                V[i][j] = frand();
        for (unsigned i = 0; i < p; i++)
            Y_best[i] = F[f](X[i][0], X[i][1]);
        for (unsigned i = 0; i < n; i++)
            X_swarmBest[i] = params.X_high[f];
        float y_swarmBest = F[f](X_highVec[0], X_highVec[1]);

        bool termination = false;
        float inertia = 1.;

        for (unsigned k = 0; k < params.k_max; k++) {
            F_wrapper[f]((float *)X, X_highVec, (float *)V, (float *)X_best, Y, Y_best, X_swarmBest, termination, inertia, &params, f);
        }

        for (unsigned i = 0; i < p; i++)
        {
            for (unsigned j = 0; j < n; j++)
            {
                printf("%f\t", X[i][j]);
            }
            printf("F = %f\n", Y[i]);
        }
        getchar();
    }
}

Обновление : я пытался добавить обработку ошибок, как это

err = cudaMallocPitch (&X_d, &dpitch, width * sizeof(float), height);
if (err != cudaSuccess) {
    fprintf(stderr, cudaGetErrorString(err));
    exit(1);
}

после каждого вызова API, но он мне ничего не дал, а не вернул (я все еще получаю все результаты и программа работает до конца).

1 Ответ

3 голосов
/ 11 февраля 2012

Это неоправданно сложный кусок кода для того, что должно быть простым случаем воспроизведения, но тут же выпрыгивает:

const unsigned n = 2;
const unsigned p = 64;

unsigned length = n * p

dim3 threads; threads.x = 32;
dim3 blocks; blocks.x = (length/threads.x) + 1;

kernelInit<<<threads,blocks>>>(X_d, dpitch, width, params->X_high[f], params->X_low[f]);

Итак, вы сначала вычисляете неправильное количество блоков, а затем обращаете вспятьпорядок блоков на сетку и потоков на блок аргументов при запуске ядра.Это может привести к выходу за пределы памяти, либо к потере чего-либо в памяти графического процессора, либо к непредсказуемой ошибке запуска, которую может не заметить отсутствие обработки ошибок.Существует инструмент под названием cuda-memcheck, который поставляется с инструментарием начиная с версии CUDA 3.0.Если вы запустите его, он выдаст вам отчеты о нарушениях доступа к памяти в стиле valgrind.Вы должны привыкнуть использовать его, если вы еще этого не делаете.

Что касается бесконечных значений, то этого следует ожидать, не так ли?Ваш код начинается со значений в (0,1), а затем

X[i] = X[i] * (5.12--5.12) - -5.12

100 раз, что является грубым эквивалентом умножения на 10 ^ 100, за которым следует

X[i] = X[i] * (2.048--2.048) - -2.048

100 раз, что является грубым эквивалентом умножения на 4 ^ 100, за которым, наконец, следует

X[i] = X[i] * (5.12--5.12) - -5.12

снова.Таким образом, ваши результаты должны быть порядка 1E250, что намного больше, чем максимум 3.4E38, который является грубым верхним пределом представимых чисел в одинарной точности IEEE 754.

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