使用CUDA Thrust并行执行多个1D移动平均线

时间:2012-09-12 19:39:21

标签: cuda thrust

我不是一个有能力的程序员。只是有人对CUDA感到好奇,所以我正在做一些阅读。我跑过一个使用Thrust做移动平均线的例子:

Simple Moving Average Thrust Example

例如,它运行并且大多数工作正常。然而,从某种意义上来说它只能进行一次移动平均操作是微不足道的。

我如何并行地说352这些移动平均操作,都在同一个数据流上运行?在我看来,程序流程可能是:

  1. 生成数据&将其发送到一个CUDA核心。 (与现有代码相同 但要考虑100010000而不是30
  2. 的长度
  3. 将其从CUDA核心复制到所有其他351 CUDA 我的GTX 465核心
  4. 告诉每个CUDA核心要平均的数据项数量。 (456,...,352353354
  5. 告诉设备并行运行每个核心的平均值
  6. 回读每个核心的结果
  7. 我得到了这段代码

    // compute SMA using standard summation
    simple_moving_average(data, w, averages);
    

    让这一切成为现实,但我如何让Thrust并行完成其中的许多工作?

    我对此感兴趣的是股票数据。如果我看看GOOG的价格,我会把它放在使用所有核心的GPU中并留在那里。然后,我可以自由地进行大量处理,而无需再加载数据,只需读回每​​个核心的结果。注意:我可能不想在所有核心中使用GOOG。有些核心可能是GOOG,有些则带有其他符号,但我稍后会到达。我只是觉得如果每个核心都有足够的空间,我不希望全局内存中的股票数据。

    我认为这对于CUDA& amp;推力?

2 个答案:

答案 0 :(得分:3)

以下是使用arrayfire执行此操作的可能方法: 请注意,我并不隶属于此库。
我很确定这也可以用推力来完成 但是我用arrayfire发现这个更简单了。 如果图书馆是免费的,为什么我不能用它而不是推力?

在arrayfire中,您可以使用矩阵并行运行多个SMA操作:

unsigned n_SMAs = 1000;   // # of SMA indicators to evaluate 
unsigned len = 2000;      // # of stock prices per indicator
unsigned w = 6; // window size

// generate stock prices: [0..10] 
af::array data = af::randu(n_SMAs, len) * 10;

// compute inclusive prefix sums along colums of the matrix
af::array s = af::accum(data, 1);

// compute the average
af::array avg = (s.cols(w, af::end) - s.cols(0, af::end - w)) / w;
af::eval(avg);

std::cout << avg.dims() << "\n" << avg << "\n";

如果您正在寻找,请告诉我。这就是我理解你的问题的方法:并行计算几个SMA指标

答案 1 :(得分:0)

我的理解是你对以下两种情况感兴趣:

  1. 您有很长的项目序列,并且您希望通过对不同数量的项目进行平均来计算一定数量的平均值,即使用不同的移动平均窗口长度。这是我从你原来的问题中理解的。
  2. 您有一系列序列,连续存储在内存中,您希望将它们与大小为2 * RADIUS + 1的固定平均窗口并行平均。这就是@asm提出的ArrayFire代码所做的 - 你已经接受了它。
  3. 我认为编写自己的CUDA内核来执行上述操作会更容易,而不是使用CUDA Thrust。下面是一个完整的示例,其运行方式与@asm提出的ArrayFire代码相同,因此涵盖了案例#2。修改它以涵盖案例#1将是直截了当的。

    #include <thrust/device_vector.h>
    
    #define RADIUS        3
    #define BLOCK_SIZE_X  8
    #define BLOCK_SIZE_Y  8
    
    /*******************/
    /* iDivUp FUNCTION */
    /*******************/
    int iDivUp(int a, int b){ return ((a % b) != 0) ? (a / b + 1) : (a / b); }
    
    /********************/
    /* CUDA ERROR CHECK */
    /********************/
    #define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
    inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
    {
       if (code != cudaSuccess) 
       {
          fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
          if (abort) exit(code);
       }
    }
    
    /**********/
    /* KERNEL */
    /**********/
    __global__ void moving_average(unsigned int *in, unsigned int *out, unsigned int M, unsigned int N) {
    
        __shared__ unsigned int temp[BLOCK_SIZE_Y][BLOCK_SIZE_X + 2 * RADIUS];
    
        unsigned int gindexx = threadIdx.x + blockIdx.x * blockDim.x;
        unsigned int gindexy = threadIdx.y + blockIdx.y * blockDim.y;
        unsigned int gindex  = gindexy * N + gindexx;
    
        unsigned int lindexx = threadIdx.x + RADIUS;
        unsigned int lindexy = threadIdx.y;
    
        // --- Read input elements into shared memory
        temp[lindexy][lindexx] = ((gindexx < N)&&(gindexy < M))? in[gindex] : 0;
        if (threadIdx.x < RADIUS) {
            temp[lindexy][threadIdx.x] = ((gindexx >= RADIUS)&&(gindexx < (N + RADIUS))&&(gindexy < M)) ? in[gindex - RADIUS] : 0;
            temp[lindexy][threadIdx.x + (RADIUS + min(BLOCK_SIZE_X, N - blockIdx.x * BLOCK_SIZE_X))] = (((gindexx + min(BLOCK_SIZE_X, N - blockIdx.x * BLOCK_SIZE_X)) < N)&&(gindexy < M))? in[gindexy * N + gindexx + min(BLOCK_SIZE_X, N - blockIdx.x * BLOCK_SIZE_X)] : 0;
            if ((threadIdx.y == 0)&&(gindexy < M)&&((gindexx + BLOCK_SIZE_X) < N)&&(gindexy < M)) printf("Inside 2 - tidx = %i; bidx = %i; tidy = %i; bidy = %i; lindexx = %i; temp = %i\n", threadIdx.x, blockIdx.x, threadIdx.y, blockIdx.y, threadIdx.x + (RADIUS + BLOCK_SIZE_X), temp[lindexy][threadIdx.x + (RADIUS + BLOCK_SIZE_X)]);
        }
    
        __syncthreads();
    
        // --- Apply the stencil
        unsigned int result = 0;
        for (int offset = -RADIUS ; offset <= RADIUS ; offset++) {
            result += temp[lindexy][lindexx + offset];
        }
    
        // --- Store the result
        out[gindexy * N + gindexx] = result;
    }
    
    /********/
    /* MAIN */
    /********/
    int main() {
    
        const unsigned int M        = 2;
        const unsigned int N        = 4 + 2 * RADIUS;
    
        const unsigned int constant = 3;
    
        thrust::device_vector<unsigned int> d_in(M * N, constant);
        thrust::device_vector<unsigned int> d_out(M * N);
    
        dim3 GridSize(iDivUp(N, BLOCK_SIZE_X), iDivUp(M, BLOCK_SIZE_Y));
        dim3 BlockSize(BLOCK_SIZE_X, BLOCK_SIZE_Y);
        moving_average<<<GridSize, BlockSize>>>(thrust::raw_pointer_cast(d_in.data()), thrust::raw_pointer_cast(d_out.data()), M, N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
    
        thrust::host_vector<unsigned int> h_out = d_out;
    
        for (int j=0; j<M; j++) {
            for (int i=0; i<N; i++)
                printf("Element j = %i; i = %i; h_out = %i\n", j, i, h_out[N*j+i]);
        }
    
        return 0;
    
    }