CUDA学习笔记9——CUDA 共享内存 / Shared Memory

由于共享内存拥有仅次于寄存器的读写速度,比全局内存快得多。因此,能够用共享内存访问替换全局内存访问的场景都可以考虑做对应的优化。

不利用共享内存的矩阵乘法

不利用共享内存的矩阵乘法的直接实现。每个线程读取A的一行和B的一列,并计算C的相应元素,如图。
访问次数 :
从全局内存中读取A的次数为B.width,读取B的次数为A.height。

在这里插入图片描述

#include <stdio.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "error.cuh"
#include <stdlib.h>
#include<math.h>
#include <malloc.h> 
#include <stdlib.h>//利用share memory 和统一内存优化矩阵乘
#define M 80
#define N 2000// 线程块尺寸
#define BLOCK_SIZE 16///-----------没有共享内存的矩阵乘法-----------
// 矩阵以行为主的顺序存储:
// M(row, col) = *(M.elements + row * M.stride + col)
typedef struct
{int width;int height;float* elements;
} Matrix;// MatMul()调用的矩阵乘法内核
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)
{// 每个线程通过将结果累积到Cvalue中来计算C的一个元素float Cvalue = 0;int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;for (int e = 0; e < A.width; ++e)Cvalue += A.elements[row * A.width + e] * B.elements[e * B.width + col];C.elements[row * C.width + col] = Cvalue;
}// 矩阵乘法核的前向声明
//__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);
// 矩阵乘法-主机代码
//矩阵维度被假定为BLOCK_SIZE的倍数
void MatMul(const Matrix A, const Matrix B, Matrix C)
{// 将A和B加载到设备内存Matrix d_A;d_A.width = A.width; d_A.height = A.height;size_t size = A.width * A.height * sizeof(float);cudaMalloc(&d_A.elements, size);cudaMemcpy(d_A.elements, A.elements, size, cudaMemcpyHostToDevice);Matrix d_B;d_B.width = B.width; d_B.height = B.height;size = B.width * B.height * sizeof(float);cudaMalloc(&d_B.elements, size);cudaMemcpy(d_B.elements, B.elements, size, cudaMemcpyHostToDevice);// 在设备内存中分配CMatrix d_C;d_C.width = C.width; d_C.height = C.height;size = C.width * C.height * sizeof(float);cudaMalloc(&d_C.elements, size);// Invoke kerneldim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);MatMulKernel <<< dimGrid, dimBlock >>>(d_A, d_B, d_C);//从设备内存中读取CcudaMemcpy(C.elements, d_C.elements, size, cudaMemcpyDeviceToHost);// 释放设备内存cudaFree(d_A.elements);cudaFree(d_B.elements);cudaFree(d_C.elements);
}int main()
{	Matrix matrix_1, matrix_2, matrix_out;int memsize = sizeof(float) * M * N;int memsize_out = sizeof(float) * M * M;matrix_1.width  = matrix_2.height =  M;matrix_2.width  = matrix_1.height = N;matrix_out.width  = matrix_out.height = M;cudaMallocHost((void**)&matrix_1.elements, memsize);cudaMallocHost((void**)&matrix_2.elements, memsize);cudaMallocHost((void**)&matrix_out.elements, memsize_out);for (int y = 0; y < N; y++)for (int x = 0; x < M; x++)matrix_1.elements[y * M + x] = (float)rand() / 1.0E5 ;for (int y = 0; y < M; y++)for (int x = 0; x < N; x++)matrix_2.elements[y * N + x] = (float)rand() / 1.0E5;//for (int y = 0; y < N; y++)//{//	printf("\n matrix_1[%d]:\n", y);//	for (int x = 0; x < M; x++)//	{//		printf("%.2f ", matrix_1.elements[y * M + x]);//	}//}//for (int y = 0; y < M; y++)//{//	printf("\n matrix_2[%d]:\n", y);//	for (int x = 0; x < N; x++)//	{//		printf("%.2f ", matrix_2.elements[y * N + x]);//	}//}cudaEvent_t start, stop_gpu;cudaEventCreate(&start);//创建事件cudaEventCreate(&stop_gpu);//创建事件cudaEventRecord(start, 0);//记录事件MatMul(matrix_1, matrix_2, matrix_out);cudaEventRecord(stop_gpu,0);//记录事件cudaEventSynchronize(stop_gpu);float time_gpu;cudaEventElapsedTime(&time_gpu, start, stop_gpu);//事件计时//printf("\n GPU time: %.4f ms \n", time_gpu);cudaEventDestroy(start);//销毁事件cudaEventDestroy(stop_gpu);for (int y = 0; y < M; y++){printf("\n matrix_out[%d]:\n", y);for (int x = 0; x < M; x++){printf("%.2f ", matrix_out.elements[y * M + x]);}}cudaFreeHost(matrix_1.elements);cudaFreeHost(matrix_2.elements);cudaFreeHost(matrix_out.elements);system("pause");return 0;
}

在这里插入图片描述

利用共享内存的矩阵乘法

每个线程块负责计算矩阵C的一个方子矩阵Csub,块内的每个线程负责计算Csub的一个元素。

Csub等于两个矩形矩阵的乘积:维度为(A.width,block_size)的A的子矩阵与Csub具有相同的行索引,维度为(A.width,block_size)的B的子矩阵与Csub具有相同的列索引。
为了适应设备的资源,这两个矩形矩阵被分割成尽可能多的尺寸为block_size的方阵,Csub被计算为这些方阵乘积的和。
首先将两个对应的方阵从全局内存加载到共享内存,其中一个线程加载每个矩阵的一个元素,然后让每个线程计算乘积的一个元素。每个线程将这些产品的结果累积到寄存器中,完成后将结果写入全局内存。
访问次数 :
通过这种方式阻塞计算,我们利用了快速共享内存并节省了大量的全局内存带宽,矩阵A只从全局内存中读取(B.width/block_size)次,矩阵B只从全局内存中读取(A.height/block_size)次。

在这里插入图片描述

#include <stdio.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "error.cuh"
#include <stdlib.h>
#include<math.h>
#include <malloc.h> 
#include <stdlib.h>//利用share memory 和统一内存优化矩阵乘#define M 80
#define N 2000// 线程块尺寸
#define BLOCK_SIZE 16///-----------矩阵乘法与共享内存-----------
// 矩阵以行为主的顺序存储:
// M(row, col) = *(M.elements + row * M.stride + col)
typedef struct
{int width;int height;float* elements;
} Matrix;
// 得到一个矩阵元素
__device__ float GetElement(const Matrix A, int row, int col)
{return A.elements[row * A.width + col];
}
// 设置一个矩阵元素
__device__ void SetElement(Matrix A, int row, int col, float value)
{A.elements[row * A.width + col] = value;
}
// 获取A的BLOCK_SIZExBLOCK_SIZE子矩阵subb,它位于A的左上角的col子矩阵和行子矩阵
__device__ Matrix GetSubMatrix(Matrix A, int row, int col)
{Matrix Asub;Asub.width = BLOCK_SIZE;Asub.height = BLOCK_SIZE;Asub.elements = &A.elements[A.width * BLOCK_SIZE * row + BLOCK_SIZE * col];return Asub;
}// 矩阵乘法核的前向声明
__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);
// 矩阵乘法-主机代码
// 矩阵维度被假定为BLOCK_SIZE的倍数
void MatMul(const Matrix A, const Matrix B, Matrix C)
{// 将A和B加载到设备内存Matrix d_A;d_A.width = A.width; d_A.height = A.height;size_t size = A.width * A.height * sizeof(float);cudaMalloc(&d_A.elements, size);cudaMemcpy(d_A.elements, A.elements, size, cudaMemcpyHostToDevice);Matrix d_B;d_B.width = B.width; d_B.height = B.height;size = B.width * B.height * sizeof(float);cudaMalloc(&d_B.elements, size);cudaMemcpy(d_B.elements, B.elements, size, cudaMemcpyHostToDevice);// 在设备内存中分配CMatrix d_C;d_C.width = C.width; d_C.height = C.height;size = C.width * C.height * sizeof(float);cudaMalloc(&d_C.elements, size);// 调用内核dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);MatMulKernel <<< dimGrid, dimBlock >>>(d_A, d_B, d_C);// 从设备内存中读取CcudaMemcpy(C.elements, d_C.elements, size, cudaMemcpyDeviceToHost);// 空闲设备内存cudaFree(d_A.elements);cudaFree(d_B.elements);cudaFree(d_C.elements);
}
// MatMul()调用的矩阵乘法内核
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)
{// 块行和列int blockRow = blockIdx.y;int blockCol = blockIdx.x;// 每个线程块计算C的一个子矩阵CsubMatrix Csub = GetSubMatrix(C, blockRow, blockCol);// 每个线程通过将结果累积到Cvalue中来计算Csub的一个元素float Cvalue = 0;// 线程行和列在Csubint row = threadIdx.y;int col = threadIdx.x;// 遍历计算Csub所需的A和B的所有子矩阵,将每对子矩阵相乘并累加结果for (int i = 0; i < (A.width / BLOCK_SIZE); ++i){// 得到A的子矩阵Matrix Asub = GetSubMatrix(A, blockRow, i);// 得到B的子矩阵BMatrix Bsub = GetSubMatrix(B, i, blockCol);// 用于存储sub和sub的共享内存tively__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];// 将subb和Bsub从设备内存加载到共享内存,每个线程加载每个子矩阵的一个元素As[row][col] = GetElement(Asub, row, col);Bs[row][col] = GetElement(Bsub, row, col);// 同步以确保在开始计算之前加载子矩阵__syncthreads();// 将subb和Bsub相乘for (int j = 0; j < BLOCK_SIZE; ++j)Cvalue += As[row][j] * Bs[j][col];// 同步以确保在下一次迭代中加载两个新的子矩阵A和B之前完成前面的计算__syncthreads();}// 将Csub写入设备内存// 每个线程写入一个元素SetElement(Csub, row, col, Cvalue);
}int main()
{	Matrix matrix_1, matrix_2, matrix_out;int memsize = sizeof(float) * M * N;int memsize_out = sizeof(float) * M * M;matrix_1.width  = matrix_2.height =  M;matrix_2.width  = matrix_1.height = N;matrix_out.width  = matrix_out.height = M;cudaMallocHost((void**)&matrix_1.elements, memsize);cudaMallocHost((void**)&matrix_2.elements, memsize);cudaMallocHost((void**)&matrix_out.elements, memsize_out);for (int y = 0; y < N; y++)for (int x = 0; x < M; x++)matrix_1.elements[y * M + x] = (float)rand() / 1.0E5 ;for (int y = 0; y < M; y++)for (int x = 0; x < N; x++)matrix_2.elements[y * N + x] = (float)rand() / 1.0E5;cudaEvent_t start, stop_gpu;cudaEventCreate(&start);//创建事件cudaEventCreate(&stop_gpu);//创建事件cudaEventRecord(start, 0);//记录事件MatMul(matrix_1, matrix_2, matrix_out);cudaEventRecord(stop_gpu,0);//记录事件cudaEventSynchronize(stop_gpu);float time_gpu;cudaEventElapsedTime(&time_gpu, start, stop_gpu);//事件计时//printf("\n GPU time: %.4f ms \n", time_gpu);cudaEventDestroy(start);//销毁事件cudaEventDestroy(stop_gpu);for (int y = 0; y < M; y++){printf("\n matrix_out[%d]:\n", y);for (int x = 0; x < M; x++){printf("%.2f ", matrix_out.elements[y * M + x]);}}cudaFreeHost(matrix_1.elements);cudaFreeHost(matrix_2.elements);cudaFreeHost(matrix_out.elements);system("pause");return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/212972.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac电脑系统活动监控:iStat Menus 中文 for Mac

iStat Menus是一款Mac操作系统上的系统监控工具&#xff0c;它提供了实时的系统状态和性能数据&#xff0c;让用户可以方便地监控和管理自己的电脑。iStat Menus以菜单栏图标的形式显示各种系统指标&#xff0c;用户可以轻松访问和查看这些信息。 以下是iStat Menus软件的一些…

SpringBoot:异步任务基础与源码剖析

官网文档&#xff1a;How To Do Async in Spring | Baeldung。 Async注解 Spring框架基于Async注解提供了对异步执行流程的支持。 最简单的例子是&#xff1a;使用Async注解修饰一个方法&#xff0c;那么这个方法将在一个单独的线程中被执行&#xff0c;即&#xff1a;从同步执…

windows11上enable WSL

Windows电脑上要配置linux&#xff08;这里指ubuntu&#xff09;开发环境&#xff0c;主要有三种方式&#xff1a; 1&#xff09;在windows上装个虚拟机&#xff08;比如vmware&#xff09;。缺点是vmware加载ubuntu后系统会变慢很多&#xff0c;而且需要通过samba来实现window…

百望云杨正道:数电时代 CFO如何带领企业完成财税数字化转型

百望云杨正道&#xff1a;数电时代 CFO如何带领企业完成财税数字化转型 谁是企业数字化转型的操盘手&#xff1f;数字时代如何通过数智变革帮助企业降本增效&#xff0c;做厚企业价值&#xff1f; 近日&#xff0c;由财能科技主办的“2023财能书院CFO年度论坛”在北京隆重举行…

绝地求生:PGC 2023 赛事直播期间最高可获:2000万G-Coins,你还不来吗?

今年PGC直播期间将有最高2000万G-Coin掉落&#xff0c;究竟花落谁家咱们拭目以待 公告原文&#xff1a;Watch PGC 2023 Live And Earn G-Coins! 如何赚取高额G-Coin&#xff1f; Throughout the PGC 2023, an astounding 20,000,000 G-Coins will be up for grabs as part of …

Navicat 技术指引 | 适用于 GaussDB 的用户权限设置

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

【Skynet 入门实战练习】游戏模块划分 | 基础功能模块 | timer 定时器模块 | logger 日志服务模块

文章目录 游戏模块基础功能模块定时器模块日志模块通用模块 游戏模块 游戏从逻辑方面可以分为下面几个模块&#xff1a; 注册和登录网络协议数据库玩法逻辑其他通用模块 除了逻辑划分&#xff0c;还有几个重要的工具类模块&#xff1a; Excel 配置导表工具GM 指令测试机器人…

微服务学习(十二):安装Minio

微服务学习&#xff08;十二&#xff09;&#xff1a;安装Minio 一、简介 MinIO 是一款基于Go语言发开的高性能、分布式的对象存储系统。客户端支持Java,Net,Python,Javacript, Golang语言。MinIO系统&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视…

VR直播如何打破视角壁垒,提升观看体验?

随着数字技术的不断发展&#xff0c;直播行业也发生了新的变革&#xff0c;VR直播也成为了直播行业中新的趋势&#xff0c;那么VR直播是如何打破视角壁垒&#xff0c;提升观看体验的呢&#xff1f; 杭州亚运会那几天&#xff0c;多项比赛热火朝天&#xff0c;无论你是参赛队伍的…

第十五届蓝桥杯(Web 应用开发)模拟赛 1 期-大学组(详细分析解答)

目录 1.动态的Tab栏 1.1 题目要求 1.2 题目分析 1.3 源代码 2.地球环游 2.1 题目要求 2.2 题目分析 2.3 源代码 3.迷惑的this 3.1 题目要求 3.2 题目分析 3.3 源代码 4.魔法失灵了 4.1 题目要求 4.2 题目分析 4.3 源代码 5.燃烧你的卡路里 5.1 题目要求 5.2…

读像火箭科学家一样思考笔记07_探月思维

1. 挑战“不可能”的科学与企业 1.1. 互联网 1.1.1. 和电网一样具有革命性&#xff0c;一旦你插上电源&#xff0c;就能让自己的生活充满活力 1.1.2. 互联网的接入可以帮助人们摆脱贫困&#xff0c;拯救生命 1.1.3. 互联网还可以提供与天气相关的信息 1.2. 用廉价、可靠的…

CCC联盟——UWB MAC(一)

本文在前面已经介绍了相关UWB的PHY之后&#xff0c;重点介绍数字钥匙&#xff08;Digital Key&#xff09;中关于MAC层的相关实现规范。由于MAC层相应涉及内容比较多&#xff0c;本文首先从介绍UWB MAC的整体框架&#xff0c;后续陆续介绍相关的网络、协议等内容。 1、UWB MAC架…