Search code examples
cudagpgpuprefix-sum

prefix scan for large arrays


I want to write a prefix scan for large arrays using the instruction in GPUgem, It's a homework for my parallel class. I did follow all the steps in the book but still my code's not working. I got it to work for array size 4096 but it's not working for larger arrays. Here is my code :

#include <stdio.h>
#include <sys/time.h>
#define THREADS 1024
typedef int mytype;

__global__ void phaseI(mytype *g_odata, mytype *g_idata, int n, mytype *aux)
{
  __shared__ mytype temp[THREADS];
  const int tid1 = threadIdx.x;
  int offset = 1;
  temp[2*tid1] = g_idata[2*tid1]; // load input into shared memory
  temp[2*tid1+1] = g_idata[2*tid1+1];
  for (int d = THREADS>>1; d > 0; d >>= 1) // build sum in place up the tree
  {
    __syncthreads();
    if (tid1 < d)
    {
      int ai = offset*(2*tid1+1)-1;
      int bi = offset*(2*tid1+2)-1;
      temp[bi] += temp[ai];
    }
    offset *= 2;
  }
  __syncthreads();
  if (tid1 == 0) {
    aux[blockIdx.x] = temp[THREADS - 1]; 
    temp[THREADS - 1] = 0;
  }
 for (int d = 1; d < THREADS; d *= 2) // traverse down tree & build scan
    {
      offset >>= 1;
      __syncthreads();
      if (tid1 < d)
      {
         int ai = offset*(2*tid1+1)-1;
         int bi = offset*(2*tid1+2)-1;
         mytype t = temp[ai];
         temp[ai] = temp[bi];
         temp[bi] += t;
      }
    }
  __syncthreads();
  g_odata[2*thid] = temp[2*thid]; // write results to device memory
  g_odata[2*thid+1] = temp[2*thid+1];
  }

__global__ void phaseII(mytype *g_odata, mytype *aux, int n)
{
  const int tid1 = threadIdx.x;
  const int B = (n / THREADS);
  int offset = 1;
 for (int d = B>>1; d > 0; d >>= 1) // build sum in place up the tree
  {
    __syncthreads();
    if (tid1 < d)
    {
      int ai = offset*(2*tid1+1)-1;
      int bi = offset*(2*tid1+2)-1;
      temp[bi] += temp[ai];
    }
    offset *= 2;
  }
  __syncthreads();
  if (tid1 == 0 && blockIdx.x == 0) {
    aux[B - 1] = 0;
  }
for (int d = 1; d < B; d *= 2) // traverse down tree & build scan
    {
      offset >>= 1;
      __syncthreads();
      if (tid1 < d)
      {
         int ai = offset*(2*tid1+1)-1;
         int bi = offset*(2*tid1+2)-1;
         mytype t = temp[ai];
         temp[ai] = temp[bi];
         temp[bi] += t;
      }
    }
  __syncthreads();  
  g_odata[2*thid] += aux[blockIdx.x];
  g_odata[2*thid+1] += aux[blockIdx.x];
}

int main(int argc, char *argv[])
{
  if (argc != 2) {
    printf("usage: %s n\n", argv[0]);
    return -1;
  }
  const int n = atoi(argv[1]);
  mytype *h_i, *d_i, *h_o, *d_o, *d_temp;
  const int size = n * sizeof(mytype);
  h_i = (mytype *)malloc(size);
  h_o = (mytype *)malloc(size);
  if ((h_i == NULL) || (h_o == NULL)) {
    printf("malloc failed\n");
    return -1;
  }
  for (int i = 0; i < n; i++) {
    h_i[i] = i;
    h_o[i] = 0;
  }
  cudaMalloc(&d_i, size);
  cudaMalloc(&d_temp, (n / THREADS) );
  cudaMalloc(&d_o, size);
  cudaMemset(d_o, 0, size);
  cudaMemset(d_temp, 0, (n / THREADS));
  cudaMemcpy(d_i, h_i, size, cudaMemcpyHostToDevice);
  int blocks = n / THREADS;
  phaseI<<<blocks, THREADS / 2 >>>(d_o, d_i, n, d_temp);
  phaseII<<<blocks, THREADS / 2>>>(d_o, d_temp, n);
  cudaThreadSynchronize();
  cudaMemcpy(h_o, d_o, size, cudaMemcpyDeviceToHost);
  printf("\n");
  for (int i = 0; i < n ; i++) {
    printf(" %d", h_o[i]); 
  }
  printf("\n\n");

  return 0;
}

Does anyone have any idea what I'm doing wrong?


Solution

  • One possible error I see in your code is here:

      aux[thid] = temp[THREADS]; 
    

    If your temp array is temp[1024], as you say, and each block has 1024 threads, as you say, then if THREADS is 1024, temp[THREADS] will access your shared memory array out-of-bounds (one past the end.) An array of 1024 elements only has valid indices from 0 to 1023.

    Beyond that, it seems like you're asking how to take the last element out of a shared memory array (temp) and place it in a position in a (presumably global) aux array, which has one element for each block.

    Here's a fully worked example:

    $ cat t831.cu
    #include <stdio.h>
    
    #define THREADS 1024
    #define BLOCKS    20
    
    __global__ void kernel(int *aux){
    
      __shared__ int temp[THREADS];
      temp[threadIdx.x] = threadIdx.x + blockIdx.x;
      __syncthreads();
      if (threadIdx.x == 0)
        aux[blockIdx.x] = temp[THREADS-1];
    }
    
    int main(){
    
      int *h_data, *d_data;
      const int dsize = BLOCKS*sizeof(int);
      h_data=(int *)malloc(dsize);
      cudaMalloc(&d_data, dsize);
      memset(h_data, 0, dsize);
      cudaMemset(d_data, 0, dsize);
      kernel<<<BLOCKS, THREADS>>>(d_data);
      cudaMemcpy(h_data, d_data, dsize, cudaMemcpyDeviceToHost);
      for (int i = 0; i < BLOCKS; i++) printf("%d, ", h_data[i]);
      printf("\n");
      return 0;
    }
    
    $ nvcc -o t831 t831.cu
    $ cuda-memcheck ./t831
    ========= CUDA-MEMCHECK
    1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042,
    ========= ERROR SUMMARY: 0 errors
    $