总和没有正确存储

时间:2012-09-18 08:50:02

标签: c parallel-processing cuda

这是我在CUDA计划和并行开发方面的第二次尝试。

我试图找到为什么点积两个阵列。

示例:

给出

A = [1 2 3] 
B = [4 5 6]

C[0] = (1)(4) + (1)(5) + (1)(6)  
C[1] = (2)(4) + (2)(5) + (2)(6)  
C[2] = (3)(4) + (3)(5) + (3)(6)  

我初始化2个数组A和B,随机填充一个范围内的元素,然后我将A中的每个元素与B中的每个元素相乘,并将产品的总和存储在第三个数组中标识为C。我已将数组的A,B和C大小设置为100。

这给了我10'000次乘法,这是我使用100个块和128个线程(由于经线大小)并行化的。

这是我的核心功能:

__global__ void kernel(float *a, float *b, float *c, const int N) {
    if( threadIdx.x < N ) 
        c[blockIdx.x] += a[blockIdx.x] * b[threadIdx.x];
}

这是我的推理,因为聚合必须在C中累积,其与A中的数据透视索引具有相同的索引,因此我可以重用blockidx.x而且应该'正常工作;但事实并非如此。

我怀疑C索引在线程发生变化时被清除或不被共享,但我真的不确定为什么我会寻求建议。

这是完整的代码,我明确地避免了HANDLE_ERROR函数包装器的简短性

#include <stdio.h>
#include <cuda.h>
#include <time.h>

#define M 100

__global__ void kernel(float *a, float *b, float *c, const int N) {
    if(threadIdx.x < N) 
        c[blockIdx.x] += a[blockIdx.x] * b[threadIdx.x];
}

void init_array(float*, const int);
void fill_array(float*, const int, const float); 
void print_array(float*, const int, *char);

int main (void) {
    srand( time(NULL) );

    float a[M], b[M], c[M] = { 0.0 };
    float *dev_a, *dev_b, *dev_c;
    const int S = sizeof(float) * M;

    init_array(a, M);
    init_array(b, M);

    print_array(a, M, "a");
    print_array(b, M, "b");
    print_array(c, M, "c");

    cudaMalloc((void**)&dev_a, S);
    cudaMalloc((void**)&dev_b, S);
    cudaMalloc((void**)&dev_c, S);

    cudaMemcpy(dev_a, a, S, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, S, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_c, c, S, cudaMemcpyHostToDevice);

    kernel<<<M, M + 28>>>(dev_a, dev_b, dev_c, M);

    cudaMemcpy(c, dev_c, S, cudaMemcpyDeviceToHost);

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);

    print_array(c, M, "c");

    return 0;
}

void init_array(float *a, const int N) {
   int i;  
   for(i=0; i<N; i++)
       a[i] = rand() % M + 1;
}

void fill_array(float *a, const int N, const float v) {
   int i;  
   for(i=0; i<N; i++)
       a[i] = v;
}

void print_array(float *a, const int N, char *d) {
   int i;  
   for(i=0; i<N; i++)
       printf("\n%s[%d]: %f",d, i, a[i]);
}

3 个答案:

答案 0 :(得分:1)

将结果累积到C时,您会互相排斥。您不能让多个线程更新相同的数组索引。修复它的一种方法是使用原子指令,例如atomicAdd(..)。

为什么这不起作用的原因是块0中的线程0和1更新C数组中的相同位置。你得到了竞争条件。

答案 1 :(得分:1)

使用cuBLAS会更简单:cublasSdot()例程可以满足您的要求(即点积两个向量)。

这无助于您学习如何编写并行代码或在CUDA中工作,但它会为您提供良好的性能,并将针对不同的GPU进行优化。最佳做法是尽可能使用库,除非有充分的理由不这样做。

另一个答案指出你需要使用原子或其他方法来避免竞争。一种更有效的方法是让每个线程计算一个部分结果,进行逐块减少(参见SDK中的示例),最后让每个块中的一个线程在全局内存中进行原子添加以累积来自不同区块的结果。

答案 2 :(得分:-2)

您的代码将多个值写入全局内存中的同一位置,而无需同步和考虑。您可以使用某种关键部分来修复它。这是代码做你想要的:

#include <stdio.h>
#include <cuda.h>
#include <time.h>

#define M 5

__global__ void kernel(float *a, float *b, float *c, const int N) {
    __shared__ int lock;
    lock=0u;
    if(threadIdx.x < N) {
        __syncthreads();
        float mult = a[blockIdx.x] * b[threadIdx.x];

        int leaveLoop = 0;
        while (leaveLoop==0) {
            if (atomicExch(&lock, 1u) == 0u) {
                //critical section
                c[blockIdx.x] += mult;
                leaveLoop = 1;
                atomicExch(&lock,0u);
            }
        }
        __syncthreads();
    }
}

void init_array(float *a, const int N) {
    int i;  
    for(i=0; i<N; i++)
        a[i] = rand() % M + 1;
}

void fill_array(float *a, const int N, const float v) {
    int i;  
    for(i=0; i<N; i++)
        a[i] = v;
}

void print_array(float *a, const int N, char *d) {
    int i;  
    for(i=0; i<N; i++)
        printf("\n%s[%d]: %f",d, i, a[i]);
}

int main (void) {
    srand( time(NULL) );

    float a[M], b[M], c[M] = { 0.0 };
    float *dev_a, *dev_b, *dev_c;
    const int S = sizeof(float) * M;

    init_array(a, M);
    init_array(b, M);

    print_array(a, M, "a");
    print_array(b, M, "b");
    print_array(c, M, "c");

    cudaMalloc((void**)&dev_a, S);
    cudaMalloc((void**)&dev_b, S);
    cudaMalloc((void**)&dev_c, S);

    cudaMemcpy(dev_a, a, S, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, S, cudaMemcpyHostToDevice);
    cudaMemcpy(dev_c, c, S, cudaMemcpyHostToDevice);

    kernel<<<M, M + 28>>>(dev_a, dev_b, dev_c, M);

    cudaMemcpy(c, dev_c, S, cudaMemcpyDeviceToHost);

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);

    print_array(c, M, "c");

    return 0;
}

为了实现聚合数组乘法之和的目标,我建议你尽可能使用@Tom提出的建议。我编写这段代码是因为有时候使用代码而不是外部库更容易。