用CUDA计算递归数组

用CUDA计算递归数组,cuda,Cuda,我有一个递归数组定义。 随它去吧 A(x, y + 1) = f(A(x - 1, y), A(x, y), A(x + 1, y)) 第一层已初始化 A(x, 0) = g(x) 我想用CUDA逐层计算这样一个数组。问题是做这些事情的首选方式是什么。单内核是否应该为y计算in中的数组A(tid,y)[1,高度)在每一步上同步?还是只计算一个点,但调用多次?或者最好将问题分解为更大的独立部分?例如,此数组可以按菱形拆分,这样每个整个菱形都可以独立计算(菱形内部不同步)如果上一层菱形完成 如果

我有一个递归数组定义。 随它去吧

A(x, y + 1) = f(A(x - 1, y), A(x, y), A(x + 1, y))
第一层已初始化

A(x, 0) = g(x)
我想用CUDA逐层计算这样一个数组。问题是做这些事情的首选方式是什么。单内核是否应该为
y
计算
in
中的数组
A(tid,y)
[1,高度)
在每一步上同步?还是只计算一个点,但调用多次?或者最好将问题分解为更大的独立部分?例如,此数组可以按菱形拆分,这样每个整个菱形都可以独立计算(菱形内部不同步)如果上一层菱形完成

如果图层是2D而不是1D,情况会有所不同吗


我计划每秒计算这样一个宽度为10000(可能更少)和高度为44100的数组。如果有必要,问题实际上是3D(200x50x41100)。为了简单起见,我将其公式化为2D。

一个简单的方法可能只是从您在此处概述的内容开始:

单内核是否应该在每一步同步时为[1,高度]中的y计算数组A(tid,y)

这应该很容易实现

x“宽度”为10000是为了让GPU能够合理地处理这么多线程

对于一个复杂的
f()
函数,每秒进行44100次迭代(平均迭代时间约为22 us)可能是一个挑战。然而,对于一个相当简单的
f()函数
function,根据我下面的快速测试,这似乎是可能的。我们受益于这样一个事实:通过迭代地启动内核,隐藏了很多内核启动开销

下面是一个用推力编写的示例代码,用于演示概念验证:

$ cat t708.cu
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/for_each.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/copy.h>
#include <stdlib.h>
#include <iostream>

#define DSIZE 10000
#define YSIZE 2
#define NUM_ITER 44100
#define AVG_SIZE 3
#define DISP_WIDTH 5

struct f
{
  template <typename T>
  __host__ __device__
  void operator()(T t) {

    thrust::get<AVG_SIZE>(t)  = thrust::get<0>(t);
    thrust::get<AVG_SIZE>(t) += thrust::get<1>(t);
    thrust::get<AVG_SIZE>(t) += thrust::get<2>(t);

    thrust::get<AVG_SIZE>(t) /= AVG_SIZE;}
};

int main(){

  thrust::host_vector<float> h_A(DSIZE);
  for (int i =0; i < DSIZE; i++) h_A[i] = rand()/(float)RAND_MAX;  // A(x, 0) = g(x)
  thrust::device_vector<float> d_A[YSIZE];
  d_A[0].resize(h_A.size());
  d_A[1].resize(h_A.size());
  thrust::copy(h_A.begin(), h_A.end(), d_A[0].begin());
  thrust::copy(h_A.begin(), h_A.end(), d_A[1].begin());
  std::cout << "input left end: " << std::endl;
  thrust::copy(d_A[0].begin(), d_A[0].begin()+DISP_WIDTH, std::ostream_iterator<float>(std::cout, ","));
  std::cout << std::endl << "input right end: " << std::endl;
  thrust::copy(d_A[0].end() - DISP_WIDTH, d_A[0].end(), std::ostream_iterator<float>(std::cout, ","));
  std::cout << std::endl;

  cudaEvent_t start, stop;
  cudaEventCreate(&start); cudaEventCreate(&stop);
  int cur = 0;
  int nxt = 1;
  cudaEventRecord(start, 0);
  for (int i = 0; i < NUM_ITER; i++){
    thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(d_A[cur].begin(), d_A[cur].begin()+1, d_A[cur].begin()+2, d_A[nxt].begin()+1)), thrust::make_zip_iterator(thrust::make_tuple(d_A[cur].end()-2, d_A[cur].end()-1, d_A[cur].end(), d_A[nxt].end()-1)), f());
    cur = (cur==0) ? 1:0;  // modify for a full storage in y
    nxt = (nxt==0) ? 1:0;}
  cudaDeviceSynchronize();
  cudaEventRecord(stop, 0);
  cudaEventSynchronize(stop);
  float et;
  cudaEventElapsedTime(&et, start, stop);
  std::cout << "elapsed time: " << et << "ms" << std::endl << "output left end: " << std::endl;
  thrust::copy(d_A[cur].begin(), d_A[cur].begin()+DISP_WIDTH, std::ostream_iterator<float>(std::cout, ","));
  std::cout << std::endl << "output right end: " << std::endl;
  thrust::copy(d_A[cur].end() - DISP_WIDTH, d_A[cur].end(), std::ostream_iterator<float>(std::cout, ","));
  std::cout << std::endl;

  return 0;
}

$ nvcc -O3 -o t708 t708.cu
$ ./t708
input left end:
0.840188,0.394383,0.783099,0.79844,0.911647,
input right end:
0.865333,0.828169,0.311025,0.373209,0.888766,
elapsed time: 368.337ms
output left end:
0.840188,0.838681,0.837174,0.835667,0.83416,
output right end:
0.881355,0.883207,0.88506,0.886913,0.888766,
$
$cat t708.cu
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义DSIZE 10000
#定义iSize 2
#定义数字ITER 44100
#定义平均尺寸3
#定义显示宽度5
结构f
{
模板
__主机设备__
void运算符()(T){
get可能会让您加快速度,您会发现覆盖了一个类似的算法,它应该让您了解如何将这个推力方法转换为等效的cuda内核方法
  • 在不明显的情况下,“每一步上的同步”由cuda内核调用完成,这是一个设备范围的同步,隐含在对
    asch::for_each
    的调用中

  • 这看起来像是一种递归方程求解器。你看过这些论文了吗?谢谢,@m.s.这些论文是关于一维问题的。它们专注于优化。我只是在寻找简单明了的解决方案,我可以从中开始。