【问题标题】:Binary Matrix Reduction in CUDACUDA 中的二元矩阵缩减
【发布时间】:2019-11-13 22:36:52
【问题描述】:

对于满足特定条件的所有单元格,我必须遍历虚构矩阵 m * nadd + 1 的所有单元格。

我的幼稚解决方案如下:

#include <stdio.h>

__global__ void calculate_pi(int center, int *count) {
    int x = threadIdx.x;
    int y = blockIdx.x;

    if (x*x + y*y <= center*center) {
        *count++;
    }
}

int main() {
    int interactions;
    printf("Enter the number of interactions: ");
    scanf("%d", &interactions);

    int l = sqrt(interactions);

    int h_count = 0;
    int *d_count;

    cudaMalloc(&d_count, sizeof(int));
    cudaMemcpy(&d_count, &h_count, sizeof(int), cudaMemcpyHostToDevice);

    calculate_pi<<<l,l>>>(l/2, d_count);

    cudaMemcpy(&h_count, d_count, sizeof(int), cudaMemcpyDeviceToHost);
    cudaFree(d_count);

    printf("Sum: %d\n", h_count);

    return 0;
}

在我的用例中,interactions 的值可能非常大,无法分配l * l 的空间。

有人可以帮助我吗?欢迎提出任何建议。

【问题讨论】:

  • 我不明白你的问题——你有一个“虚构的”矩阵(假设这意味着它实际上并不存在,而不是它很复杂),然后你想有条件地递增该矩阵中的条目 - 即您想要更改不存在的矩阵中的条目。这怎么可能行得通?
  • 恕我直言,@talonmies,您完全错过了 O/P 使用 {threadIdx,blockIdx}.x 进行间接映射的棘手想法(暂时不提状态&lt;&lt;&lt; &gt;&gt;&gt;-Shevron-operators 使用的实际参数)[x,y]-values 用于(通常非常大的试验/错误)pi 生成算法(a早期 C/S 课程中的经典教科书示例),是的,想象是一种情况的合法表达,在这种情况下,主体不需要被实例化为真实的存在,但仍然是合法的感兴趣的对象,并且可以与 :o 进一步合作)跨度>
  • 我使用了虚数数组这个术语,因为我不想实例化一个矩阵,例如 50,000 x 50,000。我读到了 Reduce 技术,它可以解决我的问题,但如果我做对了,我总是需要一个巨大的数组。

标签: parallel-processing cuda nvidia pi


【解决方案1】:

您的代码至少有 2 个问题:

  1. 您的内核代码在此处使用普通添加将无法正常工作:

    *count++;
    

    这是因为多个线程同时尝试执行此操作,而 CUDA 不会自动为您排序。出于解释的目的,我们将使用atomicAdd() 解决此问题,但也可以使用其他方法。

  2. & 符号不属于这里:

    cudaMemcpy(&d_count, &h_count, sizeof(int), cudaMemcpyHostToDevice);
               ^
    

    我认为这只是一个错字,因为您在随后的cudaMemcpy 操作中正确地做到了:

    cudaMemcpy(&h_count, d_count, sizeof(int), cudaMemcpyDeviceToHost);
    
  3. 这种方法(有效地使用threadIdx.x 为一个维度和blockIdx.x 另一个维度创建一个线程的方形数组)只能在interactions 值导致l 值1024 或更少,因为 CUDA 线程块被限制为 1024 个线程,并且您在内核启动中使用 l 作为线程块的大小。要解决此问题,您需要了解如何创建任意维度的 CUDA 2D 网格,并适当地调整内核启动和内核内索引计算。现在,我们只需确保计算出的 l 值在您的代码设计范围内。

以下是解决上述问题的示例:

$ cat t1590.cu
#include <stdio.h>

__global__ void calculate_pi(int center, int *count) {
    int x = threadIdx.x;
    int y = blockIdx.x;

    if (x*x + y*y <= center*center) {
        atomicAdd(count, 1);
    }
}

int main() {
    int interactions;
    printf("Enter the number of interactions: ");
    scanf("%d", &interactions);

    int l = sqrt(interactions);
    if ((l > 1024) || (l < 1)) {printf("Error: interactions out of range\n"); return 0;}
    int h_count = 0;
    int *d_count;

    cudaMalloc(&d_count, sizeof(int));
    cudaMemcpy(d_count, &h_count, sizeof(int), cudaMemcpyHostToDevice);

    calculate_pi<<<l,l>>>(l/2, d_count);

    cudaMemcpy(&h_count, d_count, sizeof(int), cudaMemcpyDeviceToHost);
    cudaFree(d_count);
    cudaError_t err = cudaGetLastError();
    if (err == cudaSuccess){
      printf("Sum: %d\n", h_count);
      printf("fraction satisfying test:  %f\n", h_count/(float)interactions);
      }
    else
      printf("CUDA error: %s\n", cudaGetErrorString(err));
    return 0;
}
$ nvcc -o t1590 t1590.cu
$ ./t1590
Enter the number of interactions: 1048576
Sum: 206381
fraction satisfying test:  0.196820
$

我们看到代码表明计算得到的分数约为 0.2。这似乎是正确的吗?我声称根据您的测试,它似乎是正确的。您正在有效地创建一个表示lxl 维度的网格。您的测试实际上是在询问“该网格中的哪些点在一个圆圈内,中心位于网格的原点(角),半径为 l/2 ?”

从图片上看,是这样的:

并且可以合理地假设红色阴影区域略小于总面积的 0.25,因此 0.2 是对该区域的合理估计。

作为奖励,这里是减少上面第 3 项中列出的限制的代码版本:

#include <stdio.h>

__global__ void calculate_pi(int center, int *count) {
    int x = threadIdx.x+blockDim.x*blockIdx.x;
    int y = threadIdx.y+blockDim.y*blockIdx.y;

    if (x*x + y*y <= center*center) {
        atomicAdd(count, 1);
    }
}

int main() {
    int interactions;
    printf("Enter the number of interactions: ");
    scanf("%d", &interactions);

    int l = sqrt(interactions);
    int h_count = 0;
    int *d_count;
    const int bs = 32;
    dim3 threads(bs, bs);
    dim3 blocks((l+threads.x-1)/threads.x, (l+threads.y-1)/threads.y);

    cudaMalloc(&d_count, sizeof(int));
    cudaMemcpy(d_count, &h_count, sizeof(int), cudaMemcpyHostToDevice);

    calculate_pi<<<blocks,threads>>>(l/2, d_count);

    cudaMemcpy(&h_count, d_count, sizeof(int), cudaMemcpyDeviceToHost);
    cudaFree(d_count);
    cudaError_t err = cudaGetLastError();
    if (err == cudaSuccess){
      printf("Sum: %d\n", h_count);
      printf("fraction satisfying test:  %f\n", h_count/(float)interactions);
      }
    else
      printf("CUDA error: %s\n", cudaGetErrorString(err));
    return 0;
}

这是一个基于l 的二维网格,应该至少可以工作到 10 亿个interactions

【讨论】:

  • 谢谢!我不知道这些原子函数。关于Kernel,我又犯了一个小错误:...int xc = x - center;int yc = y - center;if (xc*xc + yc*yc &lt; center*center) {...
  • 关于 Pi 的近似值,应该是这样的:printf("PI: %lf\n", 4.0 * h_count / (l * l));
猜你喜欢
  • 2013-06-06
  • 1970-01-01
  • 2021-11-17
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
  • 1970-01-01
  • 2013-12-27
相关资源
最近更新 更多