希利斯和斯蒂尔:核心功能

时间:2013-11-01 22:41:47

标签: cuda gpu

有人可以帮助理解Hillis& Steele:内核函数适用于每个线程执行?

__global__ void scan(float *g_odata, float *g_idata, int n)
 {
    extern __shared__ float temp[]; // allocated on invocation
    int thid = threadIdx.x;
    int pout = 0, pin = 1;
    // load input into shared memory.
    // This is exclusive scan, so shift right by one and set first elt to 0
    temp[pout*n + thid] = (thid > 0) ? g_idata[thid-1] : 0;
    __syncthreads();
    for (int offset = 1; offset < n; offset *= 2)
    {
      pout = 1 - pout; // swap double buffer indices
      pin = 1 - pout;
      if (thid >= offset)
        temp[pout*n+thid] += temp[pin*n+thid - offset];
      else
        temp[pout*n+thid] = temp[pin*n+thid];
     __syncthreads();
    }
    g_odata[thid] = temp[pout*n+thid1]; // write output
}

从现在起我了解以下内容:首先,我们有pout=0, pin=1 and thid = [1,bockDim.x]。 因此,在第一次同步之前,我们有一个简单的向右移位,例如,如果我们有数组[1 | 2 | 5 | 7 ],则新数组为[0 |1 | 2 | 5 | 7 ]

我认为for loop的执行是多个实例,每个thId的每个实例。例如,如果thId=0我们将执行以下操作:

  1. thid=0

    • offset=1
    • pout = 1-0 = 1(在函数开头使用pout初始化
    • pin = 1 - 1 = 0; (使用刚刚计算的pout,e.i 1
    • temp [4] = temp [0]( else statement
    • [0 | 1 | 2 | 5 | 0]

    • offset=2

    • pout = 1-1 = 0(使用循环中上一步的pout
    • pin = 1 - 0 = 1; (刚刚计算的值
    • temp [0] = temp [4]( else statement
    • [0 | 1 | 2 | 5 | 0]

    pout和pin变量根据for循环内部的信息而不是
    更改 考虑在开始时初始化这些变量。用同样的方式
    我想象thid=1的执行。

  2. thid=1

    • offset=1
    • pout = 1 - 0 = 1(在函数开头使用pout初始化
    • pin = 1 - 1 = 0
    • temp [4 + 1] = temp [0 + 1-1]( if statement )???? 内存超出临界值 ????
  3. 任何人都可以直观地举例说明它是如何执行的吗?另外,当要执行最后一个代码语句时,将使用哪个pout值?

2 个答案:

答案 0 :(得分:2)

答案 1 :(得分:-1)

好的,这已经有5年了。但是,我想回答这个问题,因为我只是花了一些时间自己弄清楚了。也许这仍然可以帮助某人...

  1. 关于“临时内存超出范围”:您需要分配的共享内存是输入数组g_idata的两倍。

  2. 我认为,代码需要稍作更改才能起作用。我附上了一个有效的例子。使用nvcc -std=c++11进行编译。

https://github.com/fg91/learning_cuda/blob/master/notes/lesson_3/scanHillisSteele.cu

#include <cuda.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <iostream>

__global__ void scanHillisSteele(int *d_out, int *d_in, int n) {
  int idx = threadIdx.x;
  extern __shared__ int temp[];
  int pout = 0, pin = 1;

  temp[idx] = (idx > 0) ? d_in[idx - 1] : 0;
  __syncthreads();

  for (int offset = 1; offset < n; offset *= 2) {
    // swap double buffer indices
    pout = 1 - pout;
    pin = 1 - pout;
    if (idx >= offset) {
      temp[pout*n+idx] = temp[pin*n+idx - offset] + temp[pin*n+idx];  // changed line
    } else {
      temp[pout*n+idx] = temp[pin*n+idx];
    }
    __syncthreads();
  }
  d_out[idx] = temp[pout*n+idx];
}

int main() {
  const int ARRAY_SIZE = 10;
  const int ARRAY_BYTES = ARRAY_SIZE * sizeof(int);

  // generate the input array on the host
  int h_in[ARRAY_SIZE]{1, 2, 5, 7, 8, 10, 11, 12, 15, 19};
  int h_out[ARRAY_SIZE];

  // declare GPU memory pointers
  int * d_in;
  int * d_out;

  // allocate GPU memory
  cudaMalloc((void **) &d_in, ARRAY_BYTES);
  cudaMalloc((void **) &d_out, ARRAY_BYTES);

  // transfer the array to the GPU
  cudaMemcpy(d_in, h_in, ARRAY_BYTES, cudaMemcpyHostToDevice);

  // launch the kernel
  scanHillisSteele<<<1, ARRAY_SIZE, 2 * ARRAY_BYTES>>>(d_out, d_in, ARRAY_SIZE);
  cudaDeviceSynchronize();

  // transfer the resulting array to the cpu
  cudaMemcpy(h_out, d_out, ARRAY_BYTES, cudaMemcpyDeviceToHost);

  // print out the input and resulting array
  std::cout << "Input:" << std::endl;
  for (int i = 0; i < ARRAY_SIZE; ++i) {
    std::cout << h_in[i] << " " << std::flush;
  }
  std::cout << std::endl << "Exclusive scan with operation +:" << std::endl;
  for (int i = 0; i < ARRAY_SIZE; ++i) {
    std::cout << h_out[i] << " " << std::flush;
  }
  std::cout << std::endl;

  // free GPU memory allocation
  cudaFree(d_in);
  cudaFree(d_out);

  return 0;
}