cuda编程实例2-矩阵相加

2 矩阵相加

使用二维网格以及二维块来编写矩阵加法核函数。

其中进行矩阵相加的核函数的最关键步骤是将每个线程从它的线程索引映射到全局线性内存索引中。

image-20220925225912543

注意到,ix实际上表示的是列的变化,iy实际上是表示行的变化,nx和ny表示的是矩阵的维度。说明一行

所以idx=iy*nx+ix;

每一行有nx个数字,iy表示行的变化,ix表示在这行的某个位置,所以idx表示的是在整个数据中的索引。

  • 在CPU上实现矩阵相加

    其实就是定义三个矩阵的数组,以全局索引的形式保存数据,ic=ia+ib;

    就是对于数组中的每一个位置上的数,进行一对一相加。当一行的数据都加和之后,那么在第二行数据中,ia,ib,ic指针的位置需要指向第二行,因为一行的数据为nx,所以指针同步增加nx

    void sumMatrixOnHost(float* a, float* b, float* c, const int nx, const int ny) {
    	float* ia = a;
    	float* ib = b;
    	float* ic = c;
    	for (int iy = 0; iy < ny; iy++) {
    		for (int ix = 0; ix < nx; ix++) {
    			ic[ix] = ia[ix] + ib[ix];
    		}
    		ia += nx;
    		ib += nx;
    		ic += nx;
    	}
    }
    
  • 在GPU上实现矩阵相加

    对于每个线程,计算出它在全局索引中的位置,实现矩阵的加法操作。

    __global__ void sumMatrixOnGPU2D(float* a, float* b, float* c, const int nx, const int ny) {
    	int ix = threadIdx.x + blockDim.x * blockIdx.x;
    	int iy = threadIdx.y + blockDim.y * blockIdx.y;
    	unsigned int idx = iy * nx + ix;
    	if (ix < nx && iy < ny) {
    		c[idx] = a[idx] + b[idx];
    	}
    }
    

全代码:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<algorithm>

//随机初始化数组
void init(float* ip, float size) {
	for (int i = 0; i < size; i++) {
		ip[i] = float(rand() & 0xff) / 66.6;
	}
}

//打印数组
void printMatrix(float* a, float* b, float* c, const int nx, const int ny) {
	float* ia = a;
	float* ib = b;
	float* ic = c;
	printf("\nMatric:(%d,%d)\n", nx, ny);
	for (int iy = 0; iy < ny; iy++) {
		for (int ix = 0; ix < nx; ix++) {
			printf("%f+%f=%f", ia[ix], ib[ix], ic[ix]);
		}
		ia += nx;
		ib += nx;
		ic += nx;
		printf("\n");
	}
	printf("\n");
}
//打印矩阵之差
void printResult(float* c, float* cc, const int nx, const int ny) {
	float* ic = c;
	float* icc = cc;
	for (int iy = 0; iy < ny; iy++) {
		for (int ix = 0; ix < nx; ix++) {
			printf("%f", ic[ix] - icc[ix]);
		}
		ic += nx;
		icc += nx;
		printf("\n");
	}
	printf("\n");
}

//验证结果
void checkResult(float* hostRef, float* gpuRef, const int N) {
	double epsilon = 1.0E-8;
	int match = 1;
	for (int i = 0; i < N; i++) {
		if (abs(hostRef[i] - gpuRef[i]) > epsilon) {
			match = 0;
			printf("Array don't match");
			printf("host %5.2f gpu %5.2f at current %d\n", hostRef[i], gpuRef[i], i);
			break;
		}
	}
	if (match) {
		printf("Array match.\n\n");
		return;
	}
}

//CPU上两个矩阵相加
void sumMatrixOnHost(float* a, float* b, float* c, const int nx, const int ny) {
	float* ia = a;
	float* ib = b;
	float* ic = c;
	for (int iy = 0; iy < ny; iy++) {
		for (int ix = 0; ix < nx; ix++) {
			ic[ix] = ia[ix] + ib[ix];
		}
		ia += nx;
		ib += nx;
		ic += nx;
	}
}
__global__ void sumMatrixOnGPU2D(float* a, float* b, float* c, const int nx, const int ny) {
	int ix = threadIdx.x + blockDim.x * blockIdx.x;
	int iy = threadIdx.y + blockDim.y * blockIdx.y;
	unsigned int idx = iy * nx + ix;
	if (ix < nx && iy < ny) {
		c[idx] = a[idx] + b[idx];
	}
}

int main() {
	int dev = 0;
	cudaDeviceProp deviceprop;
	cudaGetDeviceProperties(&deviceprop, dev);
	printf("using Device :%d %s\n\n", dev, deviceprop.name);

	//设置矩阵维度
	int nx = 1 << 12;
	int ny = 1 << 12;
	int nxy = nx * ny;
	int nBytes = nxy * sizeof(float);

	//分配CPU的相关数据内存
	float* h_a, *h_b, *h_c, *h_cc;
	h_a = (float*)malloc(nBytes);
	h_b = (float*)malloc(nBytes);
	h_c = (float*)malloc(nBytes);
	h_cc = (float*)malloc(nBytes);

	//初始化数据
	init(h_a, nxy);
	init(h_b, nxy);

	//开始计时
	clock_t cpuStart = clock();
	sumMatrixOnHost(h_a, h_b, h_c, nx, ny);
	clock_t cpuEnd = clock();
	float cpuTime = (float)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
	printf("cpu time %f\n", cpuTime);

	//分配gpu内存
	float* d_a, *d_b, *d_c;
	cudaMalloc((void**)&d_a, nBytes);
	cudaMalloc((void**)&d_b, nBytes);
	cudaMalloc((void**)&d_c, nBytes);

	//初始化网格以及块大小
	dim3 block(128,1);
	dim3 grid((nx + block.x - 1) / block.x, (ny + block.y - 1) / block.y);
	//数据从cpu拷贝gpu
	cudaMemcpy(d_a, h_a, nBytes, cudaMemcpyHostToDevice);
	cudaMemcpy(d_b, h_b, nBytes, cudaMemcpyHostToDevice);
	//gpu调用核函数
	clock_t gpuStart = clock();
	sumMatrixOnGPU2D << <grid, block >> > (d_a, d_b, d_c, nx, ny);
	cudaDeviceSynchronize();
	clock_t gpuEnd = clock();
	float gpuTime = (float)(gpuEnd - gpuStart) / CLOCKS_PER_SEC;
	printf("GPU time:%f\n", gpuTime);

	// 结果从gpu再拷贝回cpu
	cudaMemcpy(h_cc, d_c, nBytes, cudaMemcpyDeviceToHost);
	checkResult(h_c, h_cc, nxy);
	//释放内存
	cudaFree(d_a); 
	cudaFree(d_b);
	cudaFree(d_c);
	free(h_a);
	free(h_b);
	free(h_c);
	free(h_cc);

	return 0;

}

执行结果:
image-20220925230906908
​ 打印array match,说明cpu上的结果与gpu并行的结果一致。

注意:此时的gpu的时间效率比gpu高,但是在windows下无法使用gettimeofday获取精确时间,使用的是clock()函数获取当前时间,在计算gpu的并行时间的时候可能有误差

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要安装CUDA Toolkit 10.2,请按照以下步骤进行操作: 1. 首先,确保您的计算机上已经安装了适合的NVIDIA显卡驱动程序。您可以通过访问NVIDIA官方网站(https://www.nvidia.com/Download/index.aspx)来下载和安装最新的驱动程序。 2. 下载CUDA Toolkit 10.2安装程序。您可以在NVIDIA官方网站的CUDA下载页面(https://developer.nvidia.com/cuda-10.2-download-archive)上找到适合您系统的安装程序。请选择与您操作系统相对应的版本进行下载。 3. 运行下载的安装程序。按照安装向导的指示进行操作,接受许可协议并选择安装选项。您可以选择自定义安装,以选择安装的组件。 4. 在安装选项中,确保选择安装CUDA开发工具包和CUDA示例。您还可以选择安装CUDA代码示例和其他组件,以满足您的需求。 5. 在安装过程中,可能会提示您安装驱动程序或其他必需的软件组件。请根据需要进行操作,并按照安装向导的指示进行操作。 6. 完成安装后,您需要配置环境变量。在系统的环境变量中添加CUDA的安装路径,以便系统可以找到CUDA的相关文件。具体的步骤会因操作系统而异。例如,在Windows系统中,您可以在系统属性的高级选项中设置环境变量。 7. 安装完成后,您可以通过编写和编译CUDA程序来验证安装是否成功。您可以使用NVIDIA的CUDA示例程序来测试。这些示例程序位于安装目录的samples文件夹中。 请注意,安装CUDA Toolkit需要一些计算机知识和经验。如果您对此不熟悉,建议您寻求专业人士的帮助或参考官方文档以获取更详细的说明。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值