CUDA矩阵乘法

2023-10-29

#include <iostream>
#include <time.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"


/*
 A * B = C
 A 矩阵大小为 m * l, 对应[0, 1, 2, ..., 1023],[0, 1, 2, ..., 1023]
 B 矩阵大小为 l * n   对应[1, 1, ..., 1]
 C 矩阵大小为 m * n
*/
const int M = 2048;
const int L = 1024;
const int N = 512;

/*cpu 上矩阵相乘 一般矩阵乘法*/
template <typename T>
void mat_dot_cpu_1(T* a, T* b, T* c, int m, int n, int l)
{
	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			double dTemp = 0.0;
			for (int k = 0; k < l; ++k)
			{
				dTemp += a[i * l + k] * b[k * n + j];
			}
			c[i * n + j] = dTemp;
		}
	}
}


/*cpu 上矩阵相乘 循环交换矩阵乘法
A数据的内存是连续访问的
每次读取A的一个元素,与B矩阵对应的一行做乘积运算,累加乘积结果到C矩阵的对应行*/
template <typename T>
void mat_dot_cpu_2(T* a, T* b, T* c, int m, int n, int l)
{
	for (int i = 0; i < m; ++i)
	{
		for (int k = 0; k < l; ++k)
		{
			double temp = a[i * l + k];
			for (int j = 0; j < n; ++j)
			{
				c[i * n + j] += temp * b[k * n + j];
			}
		}
	}
}


/*cpu 上矩阵相乘 转置矩阵乘法
要解决矩阵乘法中访存不连续、无法向量化的问题,除了内部两次循环交换的方法,还有矩阵转置的方法
对B矩阵转置,转置后,原来A矩阵一行和B矩阵一列的向量内积运算就变成了A矩阵的一行和B矩阵的一行的向量内积运算*/
template <typename T>
void mat_dot_cpu_3(T* a, T* b, T* c, int m, int n, int l)
{
	T* b_t = NULL;
	b_t = (T*)malloc(L * N * sizeof(T));
	// 将b矩阵转置成 b_t矩阵
	for (int i = 0; i < n; ++i)
	{
		for (int j = 0; j < l; ++j)
		{
			b_t[i * l + j] = b[j * n + i];
		}
	}

	for (int i = 0; i < m; ++i)
	{
		for (int j = 0; j < n; ++j)
		{
			double temp = 0.0;
			for (int k = 0; k < l; ++k)
			{
				temp += a[i * l + k] * b_t[j * l + k];
			}
			c[i * n + j] = temp;
		}
	}
	if (NULL != b_t)
	{
		free(b_t);
		b_t = NULL;
	}
}


template <typename T>
__global__  void mat_dot_gpu(T* a, T* b, T* c, int M, int N, int L)
{
	float temp = 0;
	int row = blockIdx.y * blockDim.y + threadIdx.y;
	int col = blockIdx.x * blockDim.x + threadIdx.x;
	if (row < M && col < N)
	{
		for (int k = 0; k < L; k++)
		{
			temp += a[row * L + k] * b[k * N + col];
		}
		c[row * N + col] = temp;
	}
}


int main()
{
	float a[M * L], b[L * N], c[M * N];
	for (int i = 0; i < M; ++i) 
	{
		for (int j = 0; j < L; ++j)
		{
			a[i * L + j] = j;
		}
	}

	for (int i = 0; i < L; ++i)
	{
		for (int j = 0; j < N; ++j)
		{
			b[i * N + j] = 1;
		}
	}

	float* dev_a, * dev_b, * dev_c;

	cudaMalloc(&dev_a, sizeof(float) * M * L);
	cudaMemcpy(dev_a, a, sizeof(float) * M * L, cudaMemcpyHostToDevice);

	cudaMalloc(&dev_b, sizeof(float) * L * N);
	cudaMemcpy(dev_b, b, sizeof(float) * L * N, cudaMemcpyHostToDevice);

	cudaMalloc(&dev_c, sizeof(float) * M * N);
	cudaMemcpy(dev_c, c, sizeof(float) * M * N, cudaMemcpyHostToDevice);

	dim3 threads_per_block(16, 16, 1); // A 16 x 16 block threads
	dim3 number_of_blocks(N / threads_per_block.x + 1, M / threads_per_block.y + 1, 1);

	clock_t start = clock();
	for (int i = 0; i < 100; ++i)
	{
		//mat_dot_cpu_1(a, b, c, M, N, L);
		//mat_dot_cpu_2(a, b, c, M, N, L);
		//mat_dot_cpu_3(a, b, c, M, N, L);

		// 执行kernel
		mat_dot_gpu << < number_of_blocks, threads_per_block >> > (dev_a, dev_b, dev_c, M, N, L);
	}
	cudaMemcpy(c, dev_c, sizeof(float) * M * N, cudaMemcpyDeviceToHost);
	clock_t end = clock();
	std::cout << end - start << "ms" << std::endl;

	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);

	return 0;
}

调用cublas库:

#include "cuda_runtime.h"
#include "cublas_v2.h"
#include <iostream>
#include <stdlib.h>

using namespace std;

// 定义测试矩阵的维度
int const A_ROW = 5;
int const A_COL = 6;
int const B_ROW = 6;
int const B_COL = 7;


int main()
{
	// 定义状态变量
	cublasStatus_t status;
	float* h_A, * h_B, * h_C;   //存储于内存中的矩阵
	h_A = (float*)malloc(sizeof(float) * A_ROW * A_COL);  //在内存中开辟空间
	h_B = (float*)malloc(sizeof(float) * B_ROW * B_COL);
	h_C = (float*)malloc(sizeof(float) * A_ROW * B_COL);

	// 为待运算矩阵的元素赋予 0-10 范围内的随机数
	for (int i = 0; i < A_ROW * A_COL; i++) 
	{
		h_A[i] = (float)(rand() % 10 + 1);
	}
	for (int i = 0; i < B_ROW * B_COL; i++) 
	{
		h_B[i] = (float)(rand() % 10 + 1);
	}
	// 打印待测试的矩阵
	cout << "矩阵 A :" << endl;
	for (int i = 0; i < A_ROW * A_COL; i++) 
	{
		cout << h_A[i] << " ";
		if ((i + 1) % A_COL == 0) cout << endl;
	}
	cout << endl;
	cout << "矩阵 B :" << endl;
	for (int i = 0; i < B_ROW * B_COL; i++) 
	{
		cout << h_B[i] << " ";
		if ((i + 1) % B_COL == 0) cout << endl;
	}
	cout << endl;

	float* d_A, *d_B, *d_C;    //存储于显存中的矩阵
	cudaMalloc((void**)& d_A, sizeof(float) * A_ROW * A_COL); //在显存中开辟空间
	cudaMalloc((void**)& d_B, sizeof(float) * B_ROW * B_COL);
	cudaMalloc((void**)& d_C, sizeof(float) * A_ROW * B_COL);

	cublasHandle_t handle;
	cublasCreate(&handle);
	cudaMemcpy(d_A, h_A, sizeof(float) * A_ROW * A_COL, cudaMemcpyHostToDevice); //数据从内存拷贝到显存
	cudaMemcpy(d_B, h_B, sizeof(float) * B_ROW * B_COL, cudaMemcpyHostToDevice);

	float a = 1, b = 0;
	cublasSgemm(
		handle,
		CUBLAS_OP_T,   //矩阵A的属性参数,转置,按行优先
		CUBLAS_OP_T,   //矩阵B的属性参数,转置,按行优先
		A_ROW,          //矩阵A、C的行数
		B_COL,          //矩阵B、C的列数
		A_COL,          //A的列数,B的行数,此处也可为B_ROW,一样的
		&a,             //alpha的值
		d_A,            //左矩阵,为A
		A_COL,          //A的leading dimension,此时选择转置,按行优先,则leading dimension为A的列数
		d_B,            //右矩阵,为B
		B_COL,          //B的leading dimension,此时选择转置,按行优先,则leading dimension为B的列数
		&b,             //beta的值
		d_C,            //结果矩阵C
		A_ROW           //C的leading dimension,C矩阵一定按列优先,则leading dimension为C的行数
	);
	//此时得到的结果便是C=AB,但由于C是按列优先,故此时得到的C应该是正确结果的转置
	std::cout << "计算结果的转置 ( (A*B)的转置 ):" << std::endl;

	cudaMemcpy(h_C, d_C, sizeof(float) * A_ROW * B_COL, cudaMemcpyDeviceToHost);
	for (int i = 0; i < A_ROW * B_COL; ++i)
	{
		std::cout << h_C[i] << " ";
		if ((i + 1) % B_COL == 0) std::cout << std::endl;
	}
	cudaFree(d_A);
	cudaFree(d_B);
	cudaFree(d_C);
	free(h_A);
	free(h_B);
	free(h_C);

	system("pause");
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA矩阵乘法 的相关文章

  • 如何在 Linux 中分析 PyCuda 代码?

    我有一个简单的 经过测试的 pycuda 应用程序 正在尝试对其进行分析 我尝试过 NVidia 的 Compute Visual Profiler 它运行该程序 11 次 然后发出以下错误 NV Warning Ignoring the
  • 构建 Erlang 服务器场(用于业余爱好项目)最便宜的方法是什么? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 假设我们有一个 本质上并行 的问题需要用 Erlang 软件来解决 我们有很多并行进程 每个进程都执行顺序代码 不是数字运算 并且我们向它们投入的 C
  • CUDA错误:在python中使用并行时初始化错误

    我的代码使用 CUDA 但运行速度仍然很慢 因此 我将其更改为使用 python 中的多处理 pool map 并行运行 但我有CUDA ERROR initialization error 这是函数 def step M self ite
  • 将 GPUJPEG 项目移植到 Windows

    我目前正在尝试移植 GPUJPEG 在 Sourceforge 上 http sourceforge net projects gpujpeg 库 基于 CUDA 从 Unix 到 Windows 现在我被卡住了 我不知道发生了什么或为什么
  • 是否可以在设备函数中调用cufft库调用?

    我在主机代码中使用 cuFFT 库调用 它们工作正常 但我想从内核调用 cuFFT 库 早期版本的 CUDA 没有这种支持 但是有了动态并行性 这可能吗 如果有任何关于如何实现这一目标的示例 那就太好了 尽管在 Kepler cc 3 5
  • 如何用Go语言的cgo编译Cuda源码?

    我用 cuda c 编写了一个简单的程序 它可以在 eclipse nsight 上运行 这是源代码 include
  • 在新线程中调用支持 CUDA 的库

    我编写了一些代码并将其放入它自己的库中 该库使用 CUDA 在 GPU 上进行一些处理 我正在使用 Qt 构建 GUI 前端 作为加载 GUI 的一部分 我调用 CUresult res CUdevice dev CUcontext ctx
  • Golang调用CUDA库

    我正在尝试从 Go 代码中调用 CUDA 函数 我有以下三个文件 test h int test add void test cu global void add int a int b int c c a b int test add v
  • 用于类型比较的 Boost 静态断言

    以下问题给我编译器错误 我不知道如何正确编写它 struct FalseType enum value false struct TrueType enum value true template
  • 使用 QuasirandomGenerator (对于傻瓜来说)

    我是 CUDA 的新手 我正在努力在内核中生成随机数 我知道有不同的实现 而且 在 SDK 4 1 中有一个 Niederreiter 拟随机序列生成器的示例 我不知道从哪里开始 我有点悲伤 感觉自己像个傻瓜 有人可以制作一个使用 Nied
  • Visual Studio - 过滤掉 nvcc 警告

    我正在编写 CUDA 程序 但收到令人讨厌的警告 Warning Cannot tell what pointer points to assuming global memory space 这是来自 nvcc 我无法禁用它 有没有办法过
  • 如何在 CUDA 中执行多个矩阵乘法?

    我有一个方阵数组int M 10 以便M i 定位第一个元素i th 矩阵 我想将所有矩阵相乘M i 通过另一个矩阵N 这样我就收到了方阵数组int P 10 作为输出 我看到有不同的可能性 分配不同元素的计算M i 到不同的线程 例如 我
  • 在 __device/global__ CUDA 内核中动态分配内存

    根据CUDA 编程指南 http developer download nvidia com compute cuda 3 2 prod toolkit docs CUDA C Programming Guide pdf 第 122 页 可
  • 如何在cmake中添加cuda源代码的定义

    我使用的是 Visual Studio 2013 Windows 10 CMake 3 5 1 一切都可以使用标准 C 正确编译 例如 CMakeLists txt project Test add definitions D WINDOW
  • 从 CUDA 设备写入输出文件

    我是 CUDA 编程的新手 正在将 C 代码重写为并行 CUDA 新代码 有没有一种方法可以直接从设备写入输出数据文件 而无需将数组从设备复制到主机 我假设如果cuPrintf存在 一定有地方可以写一个cuFprintf 抱歉 如果答案已经
  • __device__ __constant__ 常量

    有什么区别吗 在 CUDA 程序中定义设备常量的最佳方法是什么 在 C 主机 设备程序中 如果我想将常量定义在设备常量内存中 我可以这样做 device constant float a 5 constant float a 5 问题 1
  • CUDA - 将 CPU 变量传输到 GPU __constant__ 变量

    与 CUDA 的任何事情一样 最基本的事情有时也是最难的 所以 我只想将变量从 CPU 复制到 GPUconstant变量 我很难过 这就是我所拥有的 constant int contadorlinhasx d int main int
  • VS 程序在调试模式下崩溃,但在发布模式下不崩溃?

    我正在 VS 2012 中运行以下程序来尝试 Thrust 函数查找 include cuda runtime h include device launch parameters h include
  • “gld/st_throughput”和“dram_read/write_throughput”指标之间有什么区别?

    在 CUDA 可视化分析器版本 5 中 我知道 gld st requested throughput 是应用程序请求的内存吞吐量 然而 当我试图找到硬件的实际吞吐量时 我很困惑 因为有两对似乎合格的指标 它们是 gld st throug
  • 如何运行和理解CUDA Visual Profiler?

    我已经设置了 CUDA 5 0 并且我的 CUDA 项目运行良好 但我不知道如何使用 Visual Profiler 分析我的 CUDA 项目 如何运行它 我还需要安装更多吗 又该如何做呢 我的电脑使用Window 7 64位 CUDA 5

随机推荐

  • Apache Beam开发指南

    本指南用于指导Beam用户使用Beam SDK创建数据处理pipeline pipeline 本文会引导您用BeamSDK类构建和测试你的pipeline 本文不会详尽阐述所有内容 但可以看做一门未知的 编程语言 引导您用编程的方式构建您的
  • JDBC连接oracle RAC数据库配置

    RAC的配置如下 node1 ip地址192 168 60 132 实例名 rac1 主机名 rac1 node2 ip地址192 168 60 144 实例名 rac2 主机名 rac2 RAC服务名为oratest 我的应用服务器为ap
  • Json串的单引号和双引号问题

    今天遇到双引号和单引号的问题 json就是一段有格式的字符串 如果数据的封装与解析都是我们自己做的时候 单引号和双引号是没啥关系的 但是如果用到一些别的json相关的库的时候 或者像笔者这样是发给别人用的时候 这个就特别需要注意了 所以应该
  • 淘宝购物车页面 智能搜索框Ajax异步加载数据

    如果有朋友对本篇文章的一些知识点不了解的话 可以先阅读此篇文章 在这篇文章中 我大概介绍了一下构建淘宝购物车页面需要的基础知识 这篇文章主要探讨的是智能搜索框Ajax异步加载数据 jQuery的社区非常的活跃 许多朋友都在不同地方分享了很多
  • Java实现利用正则表达式校验手机号码,邮箱,电话号码

    需求 校验手机号码 邮箱 电话号码 实现代码 package apilambda d6 regex import java util Scanner public class RegexTest2 public static void ma
  • Linux使用gpu渲染桌面,WSL将支持GPU计算,并可运行Linux GUI应用

    原标题 WSL将支持GPU计算 并可运行Linux GUI应用 在刚刚召开的微软 Build 2020 大会上 传来不少有关 WSL 的新消息 这篇文章将一并整理 逐一介绍 其中 本月就可实现的包括以下这两项 随着 2020 年 5 月更新
  • python 爬虫 POST请求

    import requests 导入网络请求模块requests import json 导入json模块 字典类型的表单参数 data 1 能力是有限的 而努力是无限的 2 星光不问赶路人 时光不负有心人 发送网络请求 response
  • Spring Boot 集成 Redis

    Spring data redis 在 Spring 中整合 Redis jedis 采用的直连 多个线程操作的话 是不安全的 如果想要避免不安全的 使用 jedis pool 连接池 lettuce 采用netty 实例可以再多个线程中进
  • js基础一(补充)

    1 js概述 1 历史 1995年 js最早由Netscape的浏览器中出现 1996年 IE3中也出现了js 也称为JScript 1997年 ECMA组织制定了标准规范ECMAScript 2009年 JS遵循了CommonJS规范 开
  • Python:多路分支判断程序 输出成绩等级

    今天学习到了Python的if判断语句 根据输入的成绩 输出评定成绩 程序如下 score input 请输入成绩 手动输入成绩 score int score 将输入的字符串转换为数值 if score gt 90 and score l
  • 如何解压缩后缀名为zip.001,zip.002等的文件

    今天下了个PDF压缩包 发现不会解压 有如下几种方法 1 使用命令 打开dos界面 将文件目录切换至当前目录 假设文件名分别为 文件 01 zip 001 文件 01 zip 002 文件 01 zip 003 则在dos界面输入如下命令
  • Eclipse下编写C++

    Eclipse下运行C 程序 文章目录 Eclipse下运行C 程序 装CDT插件 测试运行C 程序 附加说明 众所周知Eclipse是常用于编写java的集成开发工具 但是像CodeBlocks和Dev C 这类集成开发工具就只能编写C或
  • SpringFramework核心技术一(IOC:注册一个LoadTimeWeaver)

    一 什么是LoadTimeWeaver 在LoadTimeWeaver用于由Spring动态变换的类 因为它们被装载到Java虚拟机 JVM 要启用加载时织入 请将其添加 EnableLoadTimeWeaving到您的某个 Configu
  • ansible主机连通性测试报错

    报错提示 root yx01 site packages ansible all m ping WARNING provided hosts list is empty only localhost is available Note th
  • linux下boost裁剪笔记

    目录 1 裁剪流程 2 操作过程详解 3 裁剪boost库 4 备注 boost中所有库目录 最近在开发c 项目 遇到大量使用正则表达式 由于windows上使用vs2022 工具集版本比较高对c 11属性支持的比较好 没遇到什么问题 但是
  • 8051单片机实战分析(以STC89C52RC为例)

    在第一篇到第九篇博文中 我们认识到了一些基于IO口输入与输出的基础电子器件使用 8051单片机实战分析 以STC89C52RC为例 01 点亮一个LED 8051单片机实战分析 以STC89C52RC为例 02 LED延时约5s闪烁 805
  • pinia 介绍与安装

    目录 一 什么是pinia 二 为什么要使用pinia 三 准备工作 1 基于Vue3 TS Vite创建项目 2 安装pinia 四 搭建pinia模块 1 在src下创建store目录 并创建index ts文件 2 挂载pinia 一
  • Python入门教程(三)

    一 条件控制 Python条件语句是通过一条或多条语句的执行结果 true或者false 来决定执行的代码块 1 if语句 Python中if语句格式为 if condition1 为true时将执行statement的语句 如果condi
  • powershell使用conda activate激活环境出错的三个解决方法

    可能有两个原因 一共三个解决办法 方法一 原因很有可能是上次未deactivate windows系统下在powershell终端输入 activate deactivate 如果是Mac或Linux系统输入 激活环境 source act
  • CUDA矩阵乘法

    include