【问题标题】:How to Parallelize a nested for loop using CUDA to perform a computation on a 2D Array如何使用 CUDA 并行化嵌套 for 循环以对 2D 数组执行计算
【发布时间】:2018-08-12 10:54:47
【问题描述】:

我正在进行一些研究,并且是使用 CUDA 的初学者。我使用的语言是 C 和 C++,与 Nvidia 的 CUDA 兼容的基本语言。在过去的一周里,我一直试图通过将 CUDA 与我的 C++ 代码集成来获得任何形式的加速。

据我所知,就内存分配和释放而言,我做的基本都是正确的。但是当谈到实际加速计算时,我目前从非 CUDA 实现中收到不同的结果。

此外,CUDA 实现也比普通的非 cuda 版本慢。

以下是我从中调用内核函数的函数。本质上,我将原本在这个函数中的计算移到了内核函数中,以便并行化它。 //计算输入之间的距离 void computeInput(int vectorNumber, double *dist, double **weight){

double *d_dist, **d_weight;


//cout << "Dist[0] Before: " << dist[0] << endl;

cudaMalloc(&d_dist, maxClusters * sizeof(double));
cudaMalloc(&d_weight, maxClusters * vector_length * sizeof(double));

//  cout << "Memory Allocated" << endl;

//copy variables from host machine running on CPU to Kernel running on GPU
cudaMemcpy(d_dist, dist, maxClusters * sizeof(double), cudaMemcpyHostToDevice);
cudaMemcpy(d_weight, weight, maxClusters * vector_length * sizeof(double), cudaMemcpyHostToDevice);

//  cout << "Variables copied to GPU Device." << endl;

//kernel currently being run with 1 blocks with 4 threads for each block.
//right now only a single loop is parallelized, I need to parallelize each loop individually or 2d arrays individually.
dim3 blocks(8,8);
dim3 grid(1, 1);
threadedInput<<<grid,blocks>>>(vectorNumber, d_dist, d_weight);

//  cout << "Kernel Run." << endl;  

//Waits for the GPU to finish computations
cudaDeviceSynchronize();

//cout << "Weight[0][0] : " << weight[0][0];

//copy back varaible from kernelspace on GPU to host on CPU into variable weight
cudaMemcpy(weight, d_weight, maxClusters * vector_length * sizeof(double), cudaMemcpyDeviceToHost);
cudaMemcpy(dist, d_dist, maxClusters * sizeof(double), cudaMemcpyDeviceToHost);
//  cout << "GPU Memory Copied back to Host" << endl;

cout << "Dist[0] After: " << dist[0] << endl;

cudaFree(d_dist);
cudaFree(d_weight);

//cout << " Cuda Memory Freed" << endl;
}

以下是内核函数。它正在使用节点上的权重计算距离。

我想要它做的是在单独的线程上执行循环的每次迭代。

我担心它会弄乱顺序并执行错误的计算。我已经在 Stack Overflow 和其他地方搜索了有关嵌套 for 循环并行化的帮助,但它们都没有说明我做错了什么。有什么建议吗?

__global__ void threadedInput(int vecNum, double *dist, double **weight)
{
int tests[vectors][vector_length] = {{0, 1, 1, 0},
                                     {1, 0, 0, 1},
                                     {0, 1, 0, 1},
                                     {1, 0, 1, 0}};
dist[0] = 0.0;
dist[1] = 0.0;
int indexX,indexY, incrX, incrY;
indexX = blockIdx.x * blockDim.x + threadIdx.x;
indexY = blockIdx.y * blockDim.y + threadIdx.y;
incrX = blockDim.x * gridDim.x; 
incrY = blockDim.y * gridDim.y; 

for(int i = indexY; i <= (maxClusters - 1); i+=incrY)
{
    for(int j = indexX; j <= (vectors - 1); j+= incrX)
    {       
        dist[i] += pow((weight[i][j] - tests[vecNum][j]), 2);
    }// end inner for
}// end outer for

}// end CUDA-kernel

我当前的输出:

Clusters for training input:

Vector (1, 0, 1, 0, ) Place in Bin 0

Vector (1, 1, 1, 0, ) Place in Bin 0

Vector (0, 1, 1, 1, ) Place in Bin 0

Vector (1, 1, 0, 0, ) Place in Bin 0

Weights for Node 0 connections:
0.74753098, 0.75753881, 0.74233157, 0.25246902, 

Weights for Node 1 connections:
0.00000000, 0.00000000, 0.00000000, 0.00000000, 

Categorized test input:

Vector (0, 1, 1, 0, ) Place in Bin 0

Vector (1, 0, 0, 1, ) Place in Bin 0

Vector (0, 1, 0, 1, ) Place in Bin 0

Vector (1, 0, 1, 0, ) Place in Bin 0
Time Ran: 0.96623900

预期输出(除了预期的时间应该至少快 50%)

Clusters for training input:

Vector (1, 0, 1, 0, ) Place in Bin 0

Vector (1, 1, 1, 0, ) Place in Bin 1

Vector (0, 1, 1, 1, ) Place in Bin 0

Vector (1, 1, 0, 0, ) Place in Bin 1

Weights for Node 0 connections:
0.74620975, 0.75889148, 0.74351981, 0.25379025, 

Weights for Node 1 connections:
0.75368531, 0.75637331, 0.74105526, 0.24631469, 

Categorized test input:

Vector (0, 1, 1, 0, ) Place in Bin 0

Vector (1, 0, 0, 1, ) Place in Bin 1

Vector (0, 1, 0, 1, ) Place in Bin 0

Vector (1, 0, 1, 0, ) Place in Bin 1
Time Ran: 0.00033100

【问题讨论】:

  • 您应该查看相关问题:例如stackoverflow.com/questions/37708101/…
  • 你能提供更多关于你的数据和你打算用它做的计算的信息吗?根据您的代码,您有 1 个块 (23/12=1) 的 12x12 线程。 maxClusters 是什么? vectorsvector_length 是什么?你在哪里定义它们?变量tests 是所有线程共有的吗?局部参数distweight 分别是向量和矩阵吗?它们的尺寸是多少?如果您提供更多信息会更好。
  • maxClusters、vector_length 和vectors 都是const ints。它们的值分别为 2、4、4。它们在函数声明之前在我的完整 .cu 文件中定义。您在上面看到的形式的测试对所有线程都是通用的。我将它放在内核函数中,因为我不确定它是否可以从调用内核函数的位置访问它。 dist 和 weight 都是数组,其中 dist 保存两点之间的相对距离以确定最佳匹配单元。权重是一个二维数组,包含 8 个总项目的权重,因此为 2 x 4。
  • 我编辑了我的主要帖子以包含一些更相关的代码和输出信息。有关一些其他重要信息... nvcc --version 产生 nvcc:NVIDIA (R) Cuda 编译器驱动程序 版权所有 (c) 2005-2015 NVIDIA Corporation 基于 Tue_Aug_11_14:27:32_CDT_2015 Cuda 编译工具,版本 7.5,V7.5.17 cat /另一方面, usr/local/cuda/version.txt 产生 CUDA 版本 8.0.61

标签: parallel-processing cuda


【解决方案1】:

你应该阅读一些教程,开头是:https://devblogs.nvidia.com/easy-introduction-cuda-c-and-c/

基本上每个线程都执行内核代码,所以里面应该没有循环。

我在引用:

设备代码

我们现在转到内核代码。

__global__
void saxpy(int n, float a, float *x, float *y)
{
  int i = blockIdx.x*blockDim.x + threadIdx.x;
  if (i < n) y[i] = a*x[i] + y[i];
}

在 CUDA 中,我们使用 global 声明 >specifier 定义内核,例如 saxpy。在设备代码中定义的变量不需要指定为设备变量,因为它们被假定驻留在设备上。在这种情况下,n、a 和 i 变量将由每个线程存储在 > 寄存器中,并且指针 x 和 y 必须是指向设备内存 > 地址空间的指针。这确实是真的,因为当我们从主机代码启动它时,我们将 d_x 和 d_y 传递给 >kernel。然而,前两个参数 n > 和 a 并未在主机代码中显式传输到设备。 >由于函数参数在 C/C++ 中默认按值传递,因此 >CUDA 运行时可以自动处理将这些值传输到 >device。 CUDA Runtime API 的这一特性使得在 >GPU 上启动内核变得非常自然和容易——它几乎与调用 C 函数一样。

在我们的 saxpy 内核中只有两行。如前所述,>kernel 是由多个线程并行执行的。如果我们希望每个线程>处理结果数组的一个元素,那么我们需要一种>区分和识别每个线程的方法。 CUDA 定义了变量 >blockDim、blockIdx 和 threadIdx。这些预定义变量的类型为 >dim3,类似于主机代码中的执行配置参数。 >predefined 变量 blockDim 包含每个线程块的尺寸 > 如在内核 >launch 的第二个执行配置参数中指定的。预定义变量threadIdx 和blockIdx 分别包含其线程块内的线程和网格内的线程块的索引>。表达式:

int i = blockDim.x * blockIdx.x + threadIdx.x

生成用于访问数组元素的全局索引。我们>在此示例中没有使用它,但还有 gridDim 包含>在第一个执行配置中指定的网格尺寸>启动参数。

在使用此索引访问数组元素之前,会根据元素的数量 n 检查其值,以确保没有超出范围的内存访问。如果数组中的 > 元素数不能被线程块大小整除,并且 > 因此内核启动的线程数大于 > 数组大小,则需要进行此检查。内核的第二行执行 >SAXPY 的逐元素工作,除了边界检查之外,它与 SAXPY 的主机实现的内部 >loop 相同。

if (i < n) y[i] = a*x[i] + y[i];

【讨论】:

  • 我已经看过那个教程,事实上,那个教程是我学习如何在 cuda 内存方面进行正确的内存分配和取消分配的地方。然而,它的问题在于它故意将线程和块与其计算结合在一起。另一方面,我的限制由二维数组的维度定义,它需要数据来执行计算。我正在尝试并行化这些迭代中的每一个,并且没有将并行化与答案本身结合起来。
  • 我不确定。你能写下你的程序的伪代码来解释你想要并行化什么吗?
  • 我刚刚用调用内核函数的函数以及我得到的输出与我应该得到的输出更新了我的主帖。
  • 对不起,你真的应该写你想要的伪代码。因为使用 CUDA 代码不容易看到更大的图景。我认为您还没有理解并行性在 CUDA 内核中是如何工作的。在您的示例中解释它的唯一方法是查看您想要实现的伪代码,甚至是顺序代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-30
  • 1970-01-01
  • 2018-05-28
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
相关资源
最近更新 更多