CUDA实现任意行列大小常数矩阵相乘

 如果对您有帮助请不要吝啬您的点赞哦,还有其他相关问题可以直接在下方留言,笔者看到会及时回复。

1. 前言

楼主因科研需求需要实现高性能计算,但C++及CUDA编程基础几乎为0,所以花了很大的精力来学习,为了让后来学习的朋友能够尽快的扫除简单的障碍,我将自己的CUDA学习过程分享出来,无论是Debug还是自己接触到的新知识都会相应写一点博客,希望对大家的学习有所帮助。

2. 构思

2.1 main函数

首先,我们来看main函数:

1. 规定矩阵行列大小,即:n、theta、len;

2. 创造两个数组,数组大小即为数组本身行列数的相乘,数组内容为每个元素对应的行列索引值的和,例如第一行第一列的元素,索引值为0和0(C++下标索引从0开始),所以其值为0,第一行第二列的元素索引为0和1,其值为1;

3. 建立Device上的指针,包括sig、time_delay、res,并对其分配在Device上的内存,内存大小为:指针指向数组的大小乘上数组所含元素类型的字节数,使用cudaMalloc函数实现内存分配;

4. 将在Host端的数组复制到Device上准备进行计算,同样设置内存大小,标识复制方向,这里是cudaMemcpyHostToDevice;

5. 分配进行计算的网格和线程块大小,dimBlock设置的是线程块的维度,指明包含线程数量;dimGrid设置网格大小,指明网格包含线程块数量;

6. 使用核函数<<< >>>进行计算;

7. 将计算结果复制回Host端,这时候和之前的复制反过来,还是要设置复制的内存大小为多少,方向是cudaMemcpyDevcieToHost;

8. 输出结果。

2.2 核函数

然后我们来看核函数:

1. 首先,核函数应该是无返回类型函数,因为我们是直接将结果通过cudaMemcpy函数复制回Host端;

2. 其输入设置需要包含所有我们在计算中需要的数组和变量,比如这里就是sig、time_delay、res,还有行列数theta、len、n;

3. row和col的索引分别代表我们最终获得数组的行和列数,这个是和核函数的网格、线程块大小设置对应的,简单解释一下:blockDim.y * blockIdx.y + threadIdx.y是索引y方向上的线程,blockDim.x * blockIdx.x + threadIdx.x是索引x方向上的线程,不清楚的话同样照抄就行;

4. 然后进行数组计算,可以看到在核函数内的循环是可以用if代替的,正常来说我们在Host端直接进行行列式计算时需要三个for循环,这里是将外部的两个for循环替换为了线程的索引;

5. 这里我是将二维数组用一维来表示的,由于C++基础薄弱,我对二维数组的理解不是太好,就用了最笨的办法,仅为了实现矩阵乘法目的,大家下来可以试一试res[row][col]的方法。

3. 代码及输出

#include <stdio.h>
#include <cuda_runtime.h>
#include <iostream>

using namespace std;

/*  常数的矩阵乘法  */   

__global__ void matrix_mul(int* sig, int* time_delay, int* res,int theta,int len,int n)
{
	int row = blockDim.y*blockIdx.y + threadIdx.y;
	int col = blockDim.x*blockIdx.x + threadIdx.x;

    if(row<theta && col<len)
    {
        for(int i = 0 ; i < n ;i++)
        {
            //theta x len大小的矩阵res = time_delay矩阵的row行i列 X sig矩阵i行col列
            res[row*len+col] += time_delay[row*n+i] * sig[col+i*len];  
        }   
    }
}
 
int main(int argc, char **argv)
{
    int n = 2;
    int theta = 3;
    int len = 3;

    printf("第一个矩阵是%d行%d列的矩阵A:\n",theta,n);
    int t_delay[theta*n];
    for(int i = 0 ;i<theta;i++)
    {
        for(int j = 0;j<n;j++)
        {
            t_delay[i*n+j] = i*n+j;
            cout << t_delay[i*n+j] << '\t';
        }
        cout << endl;
    }

    printf("第二个矩阵是%d行%d列的矩阵B:\n",n,len);
    int sig_[len*n];
    for(int i = 0 ;i<n;i++)
    {
        for(int j = 0;j<len;j++)
        {
            sig_[i*len+j] = i*len+j;
            cout << sig_[i*len+j] << '\t';
        }
        cout << endl;
    }


    
    int sig_out[theta*len];
    int *time_delay;
    int *sig;
    int *res;

	cudaMalloc((void**)(&time_delay), theta*n*sizeof(int));
    cudaMalloc((void**)(&sig), n*len*sizeof(int));
	cudaMalloc((void**)(&res), theta*len*sizeof(int));  //从dc开始分配ROWS*COLS*sizeof(int)大小的地址

 	cudaMemcpy(sig, sig_, n*len*sizeof(int), cudaMemcpyHostToDevice);
	cudaMemcpy(time_delay, t_delay, theta*n*sizeof(int), cudaMemcpyHostToDevice);
	dim3 dimBlock(32,32);
	dim3 dimGrid((len+dimBlock.x-1)/dimBlock.x,(theta+dimBlock.y-1)/dimBlock.y); //分配线程块大小

	matrix_mul<<<dimGrid, dimBlock>>>(sig,time_delay,res,theta,len,n);

	cudaMemcpy(sig_out,res, theta*len*sizeof(int), cudaMemcpyDeviceToHost);

    printf("得到的矩阵是%d行%d列的矩阵C:\n",theta,len);
    for(int i = 0;i<theta;i++)
    {
        for(int j = 0 ;j<len;j++)
        {
            cout << sig_out[i*len+j]<<'\t';
        }
        cout <<endl;
    }
	cudaFree(sig);
	cudaFree(time_delay);
    cudaFree(res);

	return 0;
}

输出:

4. 总结

1. 实现了常数矩阵的运算;

2. 此代码可以在常数矩阵计算的基础上进行复数矩阵计算的推导,届时将会用到CUDA的cuComplex库,但是cuComplex库的计算重载仅包含简单的复数定义、取模、取实部虚部、加减乘除,缺少指数运算之类的关键计算方法;

3. 如果使用大尺度的行列式计算可能会出现“段错误(核心已转储)”的问题,出现问题的原因经测试发现是生成sig_out数组时被认作了“C++ 虚表指针”,解决办法是对其进行专门的定义并使用malloc进行内存分配(可能是误打误撞解决的),仅供参考。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值