数值积分; CUDA开发

时间:2013-06-14 10:58:05

标签: performance cuda numerical-integration

我需要有关如何继续使用CUDA设备的计算能力以进行函数数值积分的建议。有关我的设备的一些信息低于(无关)

硬件

 Geforce GTX470; Compute Capability 2.0

问题描述

我有一个像

这样的功能
g(x) = x * f(x, a, b, c)

我需要按照给定equation

进行整合

现在我已经编写了一个积分函数,它只需要g(x),将区间分成 N 子区间,计算单个子区间的结果,然后我总结一下中央处理器。为了完成目的,我在下面提供了一个代码示例。

__device__ float function(float x, float a, float b, float c) {
   // do some complex calculation
   return result;
}
__global__ void kernel(float *d_arr, float a, float b, float c, int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    float x = (float)idx / (float)N;

    if (idx < N)  {
       d_arr[idx] = x * function(x, a, b, c);
    }
}

上面的代码仅用于演示目的,我实际上使用Romberg方法来集成我的 g(x)但是这个想法是一样的。我真正的问题是因为我没有一组值(a,b,c),我有这个集合的多个值。

我在设备内存中有一个2D数组,精确地(3,1024)3行,1024列。每列代表一个需要执行集成功能的集合。

当我必须决定是否执行1024个线程块时,问题就到了,记住一个线程相当于一个集成函数。在这种情况下,我上面写的函数是没用的。因为我想对所有值集执行并行集成,所以我必须编写一个集成函数,它可以按顺序进行集成。举个例子:

__global__ void kernel(float *d_arr, float a, float b, float c, int N) {

   int idx = blockIdx.x * blockDim.x + threadIdx.x;
   float sum = 0;
   for (int i = 0; i < N; i++) {
      float x = (float)i / (float) N;
      sum += x * function(x, a, b, c);
   } 
    d_arr[idx] = sum;
}

所以你看到了我的观点?选项A,似乎更好,但我不能使用它因为我不知道如何做多个积分然后将每个积分分配给N个线程。

你会怎么做?你能建议我,我怎样才能实现多重积分,而每个积分可以分配给N个线程?有没有更好的方法呢。

期待您的建议。

1 个答案:

答案 0 :(得分:1)

如果我正确理解您的问题,您希望与多个(1024)输入集(a,b,c)进行数值积分,并且对于每个积分,您需要N个子间隔。我们称之为输入集M的数量。

如果N足够大(比如说> 10000),你粘贴的第一个内核样本就足够了(对不同的输入集调用M次)。它是否利用所有可用的设备吞吐量取决于您的功能有多复杂。

我没知道你对d_arr []数组做了什么?通常,对于数值积分,您需要对它求和。对?你在CPU上总结结果吗?考虑使用atomicAdd(例如,如果你打算在计算上限3.0和更高的gpus上运行内核),或者如果你发现atomicAdd不够快,可以使用并行扫描。

如果N很小,最好在单个内核中启动N * M个线程。

在你的情况下,当M = 1024时,你可以让每个块处理一组输入(即设置blockSize = 1024),并将(a,b,c)输入作为数组传递给内核 - 如下所示:

__global__ void kernel(float *d_arr, float *a_array, float *b_array, float *c_array, int totalThreads, int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    float x = (float) blockIdx.x / (float) N;
    float a = a_array[threadIdx.x];
    float b = b_array[threadIdx.x];
    float c = c_array[threadIdx.x];

    if (idx < totalThreads)  {
       // what happen to this array?
       d_arr[idx] = x * function(x, a, b, c);
    }
}

同样,您稍后需要从适当的位置从d_arr中提取元素并将它们相加(对于每个积分)。

如果你的函数不是很复杂并且上面的内核变成了内存限制,你可以反过来尝试,即让每个线程块处理每个子区间 - 不同的线程块在不同的输入集上工作。内核看起来像这样:

(这个例子假设N <= 1024,但是有可能破坏你的内核以利用这种方法,即使它不是这样)

__global__ void kernel(float *d_arr, float *a_array, float *b_array, float *c_array, int totalThreads) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;

    float x = (float)threadIdx.x / (float) blockDim.x;  // N = blockDim.x

    float a = a_array[blockIdx.x];  // every thread in block accesses same memory location
    float b = b_array[blockIdx.x];
    float c = c_array[blockIdx.x];

    // d_arr has 'M' elements containing the integral for each input set.
    if (idx < totalThreads)  
    {
       atomicAdd(&d_arr[blockIdx.x], x * function(x, a, b, c));
    }
}

在上面的内核中有a_array,b_array和c_array分配在常量内存中。这将更快,因为块中的每个线程将访问相同的位置。 作为一个例子,我还用atomicAdd替换了你的d_arr写。