Estoy siguiendo una introducción al tutorial de programación paralela de CUDA y tengo algunas dificultades para obtener respuestas válidas.

Tutorial -> http://www.nvidia.com /docs/IO/116711/sc11-cuda-c-basics.pdf

Mi código, publicado a continuación, que es similar al tutorial, compila y ejecuta, sin embargo, solo se calcula correctamente el primer entero de la matriz.

Parece que hay algún tipo de problema de alineación de datos, simplemente no entiendo por qué. Además, nunca he visto operaciones matemáticas realizadas en una matriz de esta manera (agregando sin índices y confiando en el tamaño del bloque para definir los límites de bits).
Entiendo por qué funcionaría, y funciona en el tutorial, así que supongo que he pasado por alto algo. ¿Alguien me puede apuntar en la dirección correcta?

#include <iostream>
#include <cstdlib>
#include <ctime>

__global__ void add(int* a, int* b, int* c) {
  *c = *a + *b;
} 

int main(){
  srand(time(NULL));
  int n = 100;
  int size = n * sizeof(int);

  int* inputA;
  int* inputB;
  int* output;

  int* d_inputA;
  int* d_inputB;
  int* d_output;

  inputA = (int*)malloc(size);  
  inputB = (int*)malloc(size);
  output = (int*)malloc(size);

  cudaMalloc((void**)&d_inputA, size);
  cudaMalloc((void**)&d_inputB, size);
  cudaMalloc((void**)&d_output, size);

  for(int i = 0; i < n; i++) {
    inputA[i] = rand() % 100 + 1;
    inputB[i] = rand() % 500 +1;
  }

  cudaMemcpy(d_inputA, inputA, size, cudaMemcpyHostToDevice);
  cudaMemcpy(d_inputB, inputB, size, cudaMemcpyHostToDevice);

  add<<<n,1>>>(d_inputA, d_inputB, d_output);

  cudaMemcpy(output, d_output, size, cudaMemcpyDeviceToHost);

  for(int i = 0; i < n; i++) {
  std::cout << i << ": "
    << inputA[i] << " + " << inputB[i] << " = "
    << output[i] << std::endl;
  }
  free(inputA);
  free(inputB);
  free(output);

  cudaFree(d_inputA);
  cudaFree(d_inputB);
  cudaFree(d_output); 

  return 0;
}
0
mreff555 5 feb. 2017 a las 18:03

2 respuestas

La mejor respuesta

Como lo ha escrito, cada hilo en el núcleo realizará el equivalente de

__global__ void add(int* a, int* b, int* c) {
  c[0] = a[0] + b[0];
} 

Lo que debería hacer evidente por qué solo el primer elemento de la matriz tiene el valor correcto.

Si modifica el núcleo de esta manera:

__global__ void add(int* a, int* b, int* c) {
  int idx = threadIdx.x + blockDim.x * blockIdx.x;
  c[idx] = a[idx] + b[idx];
} 

Para que cada subproceso en la cuadrícula 1D calcule un índice único, debe encontrar que el núcleo hace lo que espera. El índice se calcula utilizando variables integradas de las que encontrará una discusión si sigue leyendo el material introductorio que está utilizando (por ejemplo, hasta la diapositiva 26).

2
2 revs, 2 users 97% 5 feb. 2017 a las 15:18

Su núcleo solo lee y escribe en el primer elemento de la matriz, así que eso es lo que obtiene en la salida ...

¿Quizás está asumiendo erróneamente que diferentes subprocesos obtendrían diferentes punteros a elementos consecutivos de las matrices? Bueno, así no es como funciona CUDA. Cada hilo obtiene exactamente los mismos parámetros, y solo difieren en sus identificadores de hilo y bloque:

__global__ void add(int* a, int* b, int* c) {
  int i = threadIdx.x + blockIdx.x * blockDim.x;
  c[i] = a[i] + b[i];
}

Esto debería hacerlo en el caso general. En su caso, tiene 1 subproceso por bloque y bloques n, por lo que deberá tener:

__global__ void add(int* a, int* b, int* c) {
  int i = blockDim.x;
  c[i] = a[i] + b[i];
}

... pero intenta evitar tener pocos hilos por bloque; tu rendimiento será pésimo de esa manera. Entonces, no use el código anterior; más bien, cree una cuadrícula con bloques grandes.

0
einpoklum 5 feb. 2017 a las 15:20