CUDA矩阵乘法及优化【参加CUDA线上训练营】

2023-10-29

目录

矩阵乘法

​​​​​​CPU方式

GPU方式

GPU中矩阵相乘步骤

GPU矩阵乘法代码示例

利用shared memory优化矩阵乘法

Share Memory矩阵乘法代码示例


矩阵乘法

​​​​​​CPU方式

利用三个for循环进行矩阵乘法

GPU方式

GPU中2维gird的线程索引 

 

GPU中矩阵相乘步骤

 通过并行计算,减少了两层for循环。

GPU矩阵乘法代码示例

#include <stdio.h>
#include <math.h>
#define BLOCK_SIZE 16
//二维矩阵乘法-GPU算法
__global__ void gpu_matrix_mult(int *a,int *b, int *c, int m, int n, int k)
{ 
    //计算处当前执行的线程在所有线程中的坐标
    int row = blockIdx.y * blockDim.y + threadIdx.y; 
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    //初始化不要忘记
    int sum = 0;
    //读取a矩阵的一行,b矩阵的一列,并做乘积累加
    if( col < k && row < m) 
    {
	//此处for循环是读取a矩阵每行的第i个数和b矩阵每列的第i个数
        for(int i = 0; i < n; i++) 
        {
            //a[所在行数 * a矩阵列的宽度(一行就几个元素) + 所在行的第几个   b[所在列的第几个(从0开始)*b矩阵列的宽度 + 所在列数]
            sum += a[row * n + i] * b[i * k + col];
        }
        c[row * k + col] = sum;
    }
} 
//二维矩阵乘法-cpu基本for循环算法
void cpu_matrix_mult(int *h_a, int *h_b, int *h_result, int m, int n, int k) {
    for (int i = 0; i < m; ++i) 
    {
        for (int j = 0; j < k; ++j) 
        {
            int tmp = 0.0;
            for (int h = 0; h < n; ++h) 
            {
                tmp += h_a[i * n + h] * h_b[h * k + j];
            }
            h_result[i * k + j] = tmp;
        }
    }
}
int main(int argc, char const *argv[])
{
    int m=100;
    int n=100;
    int k=100;
//申请主机cpu内存,这里等于普通的malloc()
    int *h_a, *h_b, *h_c, *h_cc;
    cudaMallocHost((void **) &h_a, sizeof(int)*m*n);
    cudaMallocHost((void **) &h_b, sizeof(int)*n*k);
    cudaMallocHost((void **) &h_c, sizeof(int)*m*k);
    cudaMallocHost((void **) &h_cc, sizeof(int)*m*k);
for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            h_a[i * n + j] = rand() % 1024;
        }
    }
for (int i = 0; i < n; ++i) {
        for (int j = 0; j < k; ++j) {
            h_b[i * k + j] = rand() % 1024;
        }
    }
//申请GPU内存
    int *d_a, *d_b, *d_c;
    cudaMalloc((void **) &d_a, sizeof(int)*m*n);
    cudaMalloc((void **) &d_b, sizeof(int)*n*k);
    cudaMalloc((void **) &d_c, sizeof(int)*m*k);
// copy matrix A and B from host to device memory  从GPU拷贝数据到CPU
    cudaMemcpy(d_a, h_a, sizeof(int)*m*n, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, h_b, sizeof(int)*n*k, cudaMemcpyHostToDevice);
unsigned int grid_rows = (m + BLOCK_SIZE - 1) / BLOCK_SIZE;
    unsigned int grid_cols = (k + BLOCK_SIZE - 1) / BLOCK_SIZE;
    dim3 dimGrid(grid_cols, grid_rows);
    dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
   
    gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k);    
//从CPU拷贝数据到GPU
    cudaMemcpy(h_c, d_c, sizeof(int)*m*k, cudaMemcpyDeviceToHost);
    //cudaThreadSynchronize();
cpu_matrix_mult(h_a, h_b, h_cc, m, n, k);
int ok = 1;
    for (int i = 0; i < m; ++i)
    {
        for (int j = 0; j < k; ++j)
        {
            if(fabs(h_cc[i*k + j] - h_c[i*k + j])>(1.0e-10))
            {
                
                ok = 0;
            }
        }
    }
if(ok)
    {
        printf("Pass!!!\n");
    }
    else
    {
        printf("Error!!!\n");
    }
// free memory 释放内存
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);
    cudaFreeHost(h_a);
    cudaFreeHost(h_b);
    cudaFreeHost(h_c);
    cudaFreeHost(h_cc);
    return 0;
}

利用shared memory优化矩阵乘法

 C = A * B,矩阵C第n行的每个元素都要读取矩阵A的第n行,矩阵C的第m列的每个元素都要读取矩阵B的第m列。

原来方法会多次从glabal memory读取同样的数据。但利用shared memory可以将global memory中的数据先存储到shared memory上,然后每次从shared memory上读取数据,加快速度。

shared memory优点是速度快,缺点是空间小。无法容纳大量数据。因此需要分多次读取。

步骤:

1、申请小块,小块进行滑动读取所有数据存储到shared memory中。

2、各个小块内部进行矩阵乘法。

3、再将各个小块矩阵乘法之后得到的数据累加。

思想:利用shared memory;将大矩阵拆分成小矩阵。 —— 分而治之

 

 

以sub=0为例子,sub=1同理。

 

 

 总结:

Share Memory矩阵乘法代码示例

#include <stdio.h>
#include <math.h>
#include "error.cuh"

#define BLOCK_SIZE 16

//普通GPU矩阵乘法运算
__global__ void gpu_matrix_mult(int *a,int *b, int *c, int m, int n, int k)
{ 
    int row = blockIdx.y * blockDim.y + threadIdx.y; 
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int sum = 0;
    if( col < k && row < m) 
    {
        for(int i = 0; i < n; i++) 
        {
            sum += a[row * n + i] * b[i * k + col];
        }
        c[row * k + col] = sum;
    }
} 

//利用shared memory矩阵乘法运算
__global__ void gpu_matrix_mult_shared_memory(int *d_m, int *d_n, int *d_result, int n)
{
    __shared__ int tile_m[BLOCK_SIZE ][BLOCK_SIZE ]; //BLOCK_SIZE 是行数或列数
    __shared__ int tile_n[BLOCK_SIZE ][BLOCK_SIZE ];
    
    int row = blockIdx.y * blockDim.y + threadIdx.y; 
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int idx = 0;
    int tmp = 0;
    
    //将矩阵数据放入shared memory中(全局索引转为小块索引)
    for(int sub = 0;sub<gridDim.x;sub++) //sub为滑动次数,滑动次数等于block x方向的个数,即gridDim.x
    {
        idx = row*n + sub*BLOCK_SIZE +threadIdx.x; //计算全局的index row*n代表该线程所在行之前行的所有数据   sub*BLOCK_SIZE +threadIdx.x表示该线程要读的数据(用小块索引)在所在行中的位置
        if(row<n && (sub*BLOCK_SIZE +threadIdx.x) < n) //保证 矩阵的x坐标小于矩阵x方向维度,滑动之后x坐标小于矩形x方向维度
        {
            tile_m[threadIdx.y][threadIdx.x] = d_m[idx];//将数据放入小块中,按小块进行索引
        }
        else
        {
             tile_m[threadIdx.y][threadIdx.x] = 0; //若不满足if条件,小块中置0
        }
        
        idx = (sub*BLOCK_SIZE + threadIdx.y)*n + col; //N矩阵idx全局索引计算
        if(col<n && (sub * BLOCK_SIZE +threadIdx.y)<n) //保证y方向坐标不超过整个矩阵y向坐标
        {
            tile_n[threadIdx.y][threadIdx.x] = d_n[idx];
        }
        else
        {
            tile_n[threadIdx.y][threadIdx.x] = 0;
        }
        __syncthreads(); //同步等待所有线程都完成
        
        for(int k = 0;k<BLOCK_SIZE ;k++)
        {
            tmp += tile_m[threadIdx.y][k] * tile_n[k][threadIdx.x];//tmp一直存在,一直累加,直到所有线程都完成计算
        }
         __syncthreads(); //同步等待所有线程都完成
    }
    
    if(row < n && col <n)
    {
        d_result[row*n + col] = tmp;
    }
    
    
    
}

int main(int argc, char const *argv[])
{
    int m=100;
    int n=100;
    int k=100;

    int *h_a, *h_b, *h_c, *h_c_shared;
    CHECK(cudaMallocHost((void **) &h_a, sizeof(int)*m*n));
    CHECK(cudaMallocHost((void **) &h_b, sizeof(int)*n*k));
    CHECK(cudaMallocHost((void **) &h_c, sizeof(int)*m*k));
    CHECK(cudaMallocHost((void **) &h_c_shared, sizeof(int)*m*k));

    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            h_a[i * n + j] = rand() % 1024;
        }
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < k; ++j) {
            h_b[i * k + j] = rand() % 1024;
        }
    }

    int *d_a, *d_b, *d_c,*d_c_shared;
    CHECK(cudaMalloc((void **) &d_a, sizeof(int)*m*n));
    CHECK(cudaMalloc((void **) &d_b, sizeof(int)*n*k));
    CHECK(cudaMalloc((void **) &d_c, sizeof(int)*m*k));
    CHECK(cudaMalloc((void **) &d_c_shared, sizeof(int)*m*k));

    // copy matrix A and B from host to device memory
    CHECK(cudaMemcpy(d_a, h_a, sizeof(int)*m*n, cudaMemcpyHostToDevice));
    CHECK(cudaMemcpy(d_b, h_b, sizeof(int)*n*k, cudaMemcpyHostToDevice));

    unsigned int grid_rows = (m + BLOCK_SIZE - 1) / BLOCK_SIZE;
    unsigned int grid_cols = (k + BLOCK_SIZE - 1) / BLOCK_SIZE;
    dim3 dimGrid(grid_cols, grid_rows);
    dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
   
    gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k);    
    CHECK(cudaMemcpy(h_c, d_c, sizeof(int)*m*k, cudaMemcpyDeviceToHost));
    
    gpu_matrix_mult_shared_memory<<<dimGrid, dimBlock>>>(d_a, d_b, d_c_shared, n);
    CHECK(cudaMemcpy(h_c_shared, d_c_shared, (sizeof(int)*m*k), cudaMemcpyDeviceToHost));

    int ok = 1;
    for (int i = 0; i < m; ++i)
    {
        for (int j = 0; j < k; ++j)
        {
            if(fabs(h_c_shared[i*k + j] - h_c[i*k + j])>(1.0e-10))
            {
                
                ok = 0;
            }
        }
    }

    if(ok)
    {
        printf("Pass!!!\n");
    }
    else
    {
        printf("Error!!!\n");
    }

    // free memory
    CHECK(cudaFree(d_a));
    CHECK(cudaFree(d_b));
    CHECK(cudaFree(d_c));
    CHECK(cudaFree(d_c_shared));
    CHECK(cudaFreeHost(h_a));
    CHECK(cudaFreeHost(h_b));
    CHECK(cudaFreeHost(h_c));
    CHECK(cudaFreeHost(h_c_shared));
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA矩阵乘法及优化【参加CUDA线上训练营】 的相关文章

  • java入门二:java流程控制

    1 用户交互Scanner java util Scanner是Java5的新特性 可以通过Scanner类来获取用户的输入 基本语法 Scanner s new Scanner System in 通过Scanner类的next 与nex
  • 软件项目管理知识回顾---网络图

    网络图 9 网络图 9 1简介 1 分类 AOA 双代号 ADM AON PDM 单代号 前导图 2 活动的逻辑管理 头到头 尾 尾到头 尾 依赖关系 3 工序 紧前 紧后 9 2绘制规则 1 两个节点只能一条线 不能是平行线 平行的话就不
  • 开源网络引擎firefly:环境搭建

    原文来自 http blog csdn net losophy article details 17000769 我自己配置的时候修改了几处 一 安装Python windows下安装Python 1 下载对应系统的python版本 现在多
  • 基于三维激光点云的树木建模(枝叶分离)

    基于三维激光点云的树木建模 2019 05 30 三维激光点云数据采集 2019 06 15 点云的枝叶分离 树枝 树干提取 枝干骨架提取 枝干骨架优化 构建三维模型 测试软件 链接 https pan baidu com s 1LhxOg
  • Java的自动装箱与拆箱详细分析

    Java的自动装箱与拆箱详细分析 1 既然说是装箱与拆箱 那么到底是装的什么 拆的什么 装箱 将基本数据类型封装起来 用他对应的引用类 包装类 来处理 拆箱 就是把引用类里面的基本数据拆出来 2 说到了基础数据类型 Java中的基础数据类型
  • Prometheus怎么监控docker容器 给我个详细的教程

    Prometheus可以通过Docker容器服务检测来监控Docker容器 具体步骤如下 1 安装Prometheus和Node Exporter 并将它们部署到Docker容器中 2 在Prometheus配置文件中添加Node Expo

随机推荐

  • 集合(四):Map接口

    文章目录 1 Map的实现类的结构 2 HashMap的底层实现原理 以JDK7为说明 3 LinkedHashMap的底层实现原理 4 Map接口 常用方法 5 TreeMap 6 Properties 1 Map的实现类的结构 Map
  • Ubuntu中文件系统根目录上的磁盘空间不足的详细解决方案

    目录 Ubuntu中文件系统根目录上的磁盘空间不足的详细解决方案 一 问题提出 二 解决问题 2 1 安装gparted管理器 2 2 打开 2 3 右键点击分区 选择调整大小 移动 一 问题提出 在使用Ubuntu时 可能会出现下图中的提
  • Protobuf初次使用小记

    本来是正在研究学习netty 教程中出现了protobuf 索性仔细研究了一番 厚积薄发 说不定哪一天就突然要用到 protobuf是一种类似于json和xml的数据传输格式 优势在于高效的序列化和反序列化 关于这个 我没有去测试 只是把整
  • AT&T汇编指令介绍

    linux中使用的AT T格式的汇编指令 所以总结一下一些比较重要的指令 1 寻址模式 有多种不同的寻址模式 允许不同形式的存储器引用 我们用符号Ea表示任意寄存器 R Ea 表示它的值 M addr 表示addr处地址的值 题目 答案 0
  • 专业级技巧:利用Vue3 provide / inject机制更轻松地完成祖先和后代组件之间的通信管理

    部分数据来源 ChatGPT 简介 在 Vue 中 使用 props 和 emit 等方式可以实现祖先和后代组件之间的通信 但是当组件层级较多时 这种方式会比较繁琐和复杂 为了解决这个问题 Vue3 提供了新的 provide inject
  • 【LeetCode第2场双周赛】

    第 2 场双周赛 A 模拟 class Solution public int sumOfDigits vector
  • 【LeetCode刷题】303 区域和检索 -数组不可变 java

    题目 给定一个整数数组 nums 处理以下类型的多个查询 计算索引 left 和 right 包含 left 和 right 之间的 nums 元素的 和 其中 left lt right 实现 NumArray 类 NumArray in
  • python3 [爬虫入门实战] 查看网站有多少个网页(站点)

    前提 进行爬虫的时候需要进行站点的爬取 再选用合适的爬虫框架 所以这里不得不需要知道一下一个网站到底有多少个网页组成 一个域名网站中到底有多少个站点 查看的方法很简单 直接百度就可以了 例如需要知道豆丁网的站点有多少个 直接在百度中输入 s
  • C# 关闭进程

    using System Diagnostics string fileName example txt Process processes Process GetProcessesByName processName foreach Pr
  • (Qt)day4

    widget h ifndef WIDGET H define WIDGET H include
  • ElasticSearch简介

    文章目录 ElasticSearch简介 正向索引和倒排索引 正向索引 倒排索引 ElasticSearch和MySQL的区别 ElasticSearch简介 什么是ElasticSearch ElasticSearch 是一款非常强大的开
  • stlink仿真器报错及处理过程记录

    项目使用stlink连接stm32f101系列的芯片 因为没有仔细阅读相关资料 出一些莫名的错 搞了大半天 前言 使用正版的stlink系列仿真器 身在山寨之国 貌似不用盗版不太合适 这里的盗版指的是别人生产来卖钱的 自己根据电路图做的不算
  • 知识的融入-增强深度学习的学习 Shades of Knowledge- Infused Learning for Enhancing Deep Learning

    知识的融入 增强深度学习的学习 摘要 SHALLOW INFUSION OF KNOWLEDGE 知识的浅层注入 Word embeddings Enriched word embeddings Deep neural language m
  • ctfshow 菜狗杯wp

    果然菜狗杯是教育我们是菜狗的 我是从第二天开始做的 这里只做了一个上午 因为下午网没了 做不了 做出来的有点少 社工也做出来挺多但是感觉社工的wp感觉就没有啥必要写了 目录 misc 签到题 损坏的压缩包 web web签到 web2 c0
  • CUDA入门笔记(三)GPU编程基础——一个典型GPU程序

    参考 优达学城 https classroom udacity com courses cs344 lessons 55120467 concepts 670611900923 一 典型GPU程序构成 一个典型GPU程序有如下几个部分 CP
  • MAC如何将[搜狗输入法]设置为默认输入法

    问题描述 即使我们下载了搜狗输入法 且在mac的 系统偏好设置 里面设置了搜狗输入法 但是当我们切屏幕的时候总是一会 简体拼音 一会 搜狗输入法 解决方式 方式1 只保留搜狗一个输入法 推荐 温馨提示 现在很多第三方输入法都已经具备中英文快
  • ResNet 论文精读 & 残差块的恒等映射 & 网络结构的解析

    论文重要知识 恒等映射 两种残差块 维度匹配和残差学习 层响应标准差 Deep Residual Learning for Image Recognition 用于图像识别的深度残差学习 目录 一 摘要 二 Introduction 介绍R
  • 深入学习java源码之Math.scalb()与 Math.powerOfTwoF()

    深入学习java源码之Math scalb 与 Math powerOfTwoF final关键字 final在Java中是一个保留的关键字 可以声明成员变量 方法 类以及本地变量 一旦你将引用声明作final 你将不能改变这个引用了 编译
  • 百度面试题——天平称重问题

    问题描述 用天平 只能比较 不能称重 从一堆小球中找出其中唯一一个较轻的 使用x 次天平 最多可以从y 个小球中找出较轻的那个 求y 与x 的关系式 解题思想 该题主要考查逻辑思维能力 我在首次遇见该题时 首先想到的对半拆分 找出其中较轻的
  • CUDA矩阵乘法及优化【参加CUDA线上训练营】

    目录 矩阵乘法 CPU方式 GPU方式 GPU中矩阵相乘步骤 GPU矩阵乘法代码示例 利用shared memory优化矩阵乘法 Share Memory矩阵乘法代码示例 矩阵乘法 CPU方式 利用三个for循环进行矩阵乘法 GPU方式 G