如何使用CUDA并行化嵌套for循环以在2D数组上执行计算
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用CUDA并行化嵌套for循环以在2D数组上执行计算相关的知识,希望对你有一定的参考价值。
我正在进行一些研究,并且非常适合使用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
你应该阅读一些教程,开头: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中,我们使用全局声明>说明符定义诸如saxpy之类的内核。设备代码中定义的变量不需要指定为>设备变量,因为假定它们驻留在设备上。在>这种情况下,n,a和i变量将由每个线程存储在>寄存器中,指针x和y必须是指向设备存储器>地址空间的指针。确实如此,因为当我们从主机代码启动它时,我们将d_x和d_y传递给>内核。然而,前两个参数n>和a未在主机代码中明确传输到设备。 >因为函数参数在C / C ++中默认按值传递,所以> CUDA运行时可以自动处理这些值到>设备的传输。 CUDA Runtime API的这一特性使得在GPU上启动内核非常自然和简单 - 它几乎与调用C函数相同。
我们的saxpy内核中只有两行。如前所述,>内核由多个线程并行执行。如果我们希望每个线程>处理结果数组的元素,那么我们需要一种>区分和识别每个线程的方法。 CUDA定义变量> blockDim,blockIdx和threadIdx。这些预定义变量的类型为> dim3,类似于主机代码中的执行配置参数。 >预定义变量blockDim包含每个线程块的维度>在内核>启动的第二个执行配置参数中指定。预定义变量threadIdx和blockIdx分别包含其线程块内线程的索引>和网格内的线程块。表达方式:
int i = blockDim.x * blockIdx.x + threadIdx.x
生成一个全局索引,用于访问数组的元素。我们>在这个例子中没有使用它,但是还有gridDim,它包含在启动的第一个执行配置>参数中指定的>网格的维度。
在使用此索引访问数组元素之前,将根据元素数n检查其值>,以确保没有越界>内存访问。如果数组中的>元素数量不能被线程块大小整除,则需要进行此检查,并且>因此内核启动的线程数大于>数组大小。内核的第二行执行> SAXPY的元素工作,而不是边界检查,它与SAXPY的主机实现的内部>循环相同。
if (i < n) y[i] = a*x[i] + y[i];
以上是关于如何使用CUDA并行化嵌套for循环以在2D数组上执行计算的主要内容,如果未能解决你的问题,请参考以下文章
使用 CUDA 在 python 中展开一个可并行化的 for 循环