Развертывание цикла CUDA в добавлении массива

#cuda

#cuda

Вопрос:

Я хочу вычислить ‘out = alpha * px beta * py’, ‘px’ и ‘py’ — это массив.*

У меня простое ядро:

 __global__ 
void saxpyGPU2( float *out, const float *px, const float *py, size_t N, float alpha,float beta )
{
    size_t i = blockDim.x*blockIdx.x   threadIdx.x;
    while (i < N)
    {
        out[i] = alpha * px[i]   beta * py[i];
        i  = blockDim.x*gridDim.x;
    } 
}
 

Это работает, поэтому я хочу развернуть цикл.

Код в cuda-handbook:

 template<const int n> 
__device__ 
void saxpy_unrolled(float *out, const float *px, const float *py, size_t N, float alpha,float beta)
{
    float x[n], y[n];
    size_t i;
    for ( i = n*blockIdx.x*blockDim.x threadIdx.x; i < N-n*blockDim.x*gridDim.x; i  = n*blockDim.x*gridDim.x ) {
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            x[j] = px[index];
            y[j] = py[index];
        }
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            out[index] = alpha*x[j] beta* y[j];
        }
    }
    // to avoid the (index<N) conditional in the inner loop, 
    // we left off some work at the end
    for ( int j = 0; j < n; j   ) {
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            if ( index<N ) {
                x[j] = px[index];
                y[j] = py[index];
            }
        }
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            if ( index<N ) out[index] = alpha*x[j] beta* y[j];
        }
    }
}

__global__ 
void saxpyGPU( float *out, const float *px, const float *py, size_t N, float alpha,float beta )
{
    saxpy_unrolled<4>( out, px, py, N, alpha ,beta);
}
 

Я не понимаю во второй ветке, когда i> N-n * blockDim.x * gridDim.x. зачем использовать внешний цикл

for ( int j = 0; j < n; j ) {
for ( int j = 0; j < n; j )....}

И я тестирую эти два ядра, первое в порядке, но второе, которое я копирую из книги, неверно.

Я создаю два массива while(i<1024) a[i] = i; b[i] = 10*i;i , и я хочу вычислить c = alpha * a beta * b, используя два ядра выше, но результат в развернутом ядре цикла равен 4.3e8 для всех элементов в c.

Это мой тестовый код:

 int main(){
int arraySize = 1024;
float* a =new float[arraySize];
float* b =new float[arraySize];
float* c =new float[arraySize];
for (int i =0;i<arraySize;i  )
{
    a[i] = 1.0* i;
    b[i] = 10.0*i;
    c[i] = 0.0;
}
float* d_a;
float* d_b;
float* d_c;
cudaMalloc((void**)amp;d_a,sizeof(float)*arraySize);
cudaMemcpy(d_a,a,sizeof(float)*arraySize,cudaMemcpyHostToDevice);
cudaMalloc((void**)amp;d_b,sizeof(float)*arraySize);
cudaMemcpy(d_b,b,sizeof(float)*arraySize,cudaMemcpyHostToDevice);
cudaMalloc((void**)amp;d_c,sizeof(float)*arraySize);
for (int i=0;i<arraySize;i  )
{
    c[i] = a[i]   b[i];
}
dim3 block_size(256,1,1);
dim3 grid_size((arraySize -1)/block_size.x 1,1,1);
float alpha = 1.0;
float beta = 1.0;
bool flag = true;
if(flag)
{
    saxpyGPU<<<grid_size,block_size>>>(d_c,d_a,d_b,arraySize,alpha,beta);
    float* temp = new float[arraySize];
    cudaMemcpy(temp,d_c,arraySize*sizeof(float),cudaMemcpyDeviceToHost);
    for (int i = 0;i<arraySize;i  )
    {
        cout<<(temp[i] - c[i])<<",";
    }
}
else
{
    saxpyGPU2<<<grid_size,block_size>>>(d_c,d_a,d_b,arraySize,alpha,beta);
    cudaMemcpy(temp,d_c,arraySize*sizeof(float),cudaMemcpyDeviceToHost);
    for (int i = 0;i<arraySize;i  )
    {
        cout<<(temp[i] - c[i])<<",";
    }
 

Эти два ядра показывают разный результат

Комментарии:

1. В чем ваш вопрос?

2. @talonmies, я отредактировал вопрос

3. Добавленный вами тестовый код является неполным и не может быть скомпилирован. В коде из руководства по CUDA нет ничего плохого. Он работает правильно.

Ответ №1:

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

 #include <thrust/random.h>
#include <thrust/device_vector.h>
#include <thrust/transform.h>
#include <thrust/copy.h>
#include <thrust/iterator/counting_iterator.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

template<const int n> 
__device__ 
void saxpy_unrolled(float *out, const float *px, const float *py, 
        size_t N, float alpha,float beta) {
    float x[n], y[n];
    size_t i;
    for ( i = n*blockIdx.x*blockDim.x threadIdx.x; 
        i < N-n*blockDim.x*gridDim.x; 
        i  = n*blockDim.x*gridDim.x ) {
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            x[j] = px[index];
            y[j] = py[index];
        }
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            out[index] = alpha*x[j] beta* y[j];
        }
    }
    for ( int j = 0; j < n; j   ) {
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            if ( index<N ) {
                x[j] = px[index];
                y[j] = py[index];
            }
        }
        for ( int j = 0; j < n; j   ) {
            size_t index = i j*blockDim.x;
            if ( index<N ) {
                out[index] = alpha*x[j]   beta*y[j];
            }
        }
    }
}

__global__ 
void saxpyGPU( float *out, const float *px, const float *py, 
        size_t N, float alpha,float beta ) {
    saxpy_unrolled<4>( out, px, py, N, alpha ,beta);
}

struct prg {
    float a, b;
    __host__ __device__
    prg(float _a=0.f, float _b=1.f) : a(_a), b(_b) {};

    __host__ __device__
    float operator()(const unsigned int n) const {
        thrust::default_random_engine rng;
        thrust::uniform_real_distribution<float> dist(a, b);
        rng.discard(n);
        return dist(rng);
    }
};

int main(void) {
    const int N = 100000;
    const float alpha = 0.12345f, beta = 0.9876f;

    prg gen(1.f, 2.f);
    thrust::device_vector<float> x(N), y(N), z(N);
    thrust::counting_iterator<unsigned int> iseqx(0);
    thrust::counting_iterator<unsigned int> iseqy(N);
    thrust::transform(iseqx, iseqx   N, x.begin(), gen);
    thrust::transform(iseqy, iseqy   N, y.begin(), gen);

    float *xp = thrust::raw_pointer_cast(amp;x[0]);
    float *yp = thrust::raw_pointer_cast(amp;y[0]);
    float *zp = thrust::raw_pointer_cast(amp;z[0]);
    dim3 blockdim(128);
    dim3 griddim(16);
    saxpyGPU<<<griddim, blockdim>>>(zp, xp, yp, N, alpha, beta);
    cudaDeviceSynchronize();

    std::vector<float> xh(N), yh(N), zh(N);
    thrust::copy(x.begin(), x.end(), xh.begin());
    thrust::copy(y.begin(), y.end(), yh.begin());
    thrust::copy(z.begin(), z.end(), zh.begin());

    float maxabserr = -1.f, maxrelerr = -1.f;
    for(int i=0; i<N; i  ) {
        float saxpyval = alpha * xh[i]   beta * yh[i];
        float abserr = fabs(zh[i]-saxpyval);
        float relerr = abserr / fmaxf(fabs(zh[i]), fabs(saxpyval));
        maxabserr = fmaxf(abserr, maxabserr);
        maxrelerr = fmaxf(relerr, maxrelerr);
    }
    std::cout.precision(10);
    std::cout << "Maximum absolute error = " << maxabserr << std::endl;
    std::cout << "Maximum relative error = " << maxrelerr << std::endl;

    return 0;
}
 

что дает мне следующее:

 $ nvcc -arch=sm_30 -o unrolled_saxpy unrolled_saxpy.cu
$ ./unrolled_saxpy 
Maximum absolute error = 2.384185791e-07
Maximum relative error = 1.1920676e-07
 

Если вы (все еще) не понимаете, почему ядро написано так, как оно есть, следуйте тому, что я показал вам в вашем предыдущем вопросе, и вручную разверните функцию saxpy. Начните с n = 1 и убедитесь, что он функционально совпадает с развернутым эквивалентом, а затем попробуйте n = 2, n = 4 и т. Д. чтобы увидеть, каково действие развертывания цикла.