当前位置: 首页 > news >正文

[8] CUDA之向量点乘和矩阵乘法

CUDA之向量点乘和矩阵乘法

  • 计算类似矩阵乘法的数学运算

1. 向量点乘

  • 两个向量点乘运算定义如下:
#真正的向量可能很长,两个向量里边可能有多个元素
(X1,Y1,Z1) * (Y1,Y2,Y3) = X1Y1 + X2Y2 + X3Y3
  • 这种原始输入是两个数组而输出却缩减为一个(单一值)的运算,在CUDA里边叫规约运算
  • 该运算对应的内核函数如下:
#include "stdio.h"
#include<iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#define N 1024
#define threadsPerBlock 512__global__ void gpu_dot(float* d_a, float* d_b, float* d_c) {//Declare shared memory__shared__ float partial_sum[threadsPerBlock];int tid = threadIdx.x + blockIdx.x * blockDim.x;//Calculate index for shared memory int index = threadIdx.x;//Calculate Partial Sumfloat sum = 0;while (tid < N){sum += d_a[tid] * d_b[tid];tid += blockDim.x * gridDim.x;}// Store partial sum in shared memorypartial_sum[index] = sum;// synchronize threads __syncthreads();// Calculating partial sum for whole block in reduce operationint i = blockDim.x / 2;while (i != 0) {if (index < i)partial_sum[index] += partial_sum[index + i];__syncthreads();i /= 2;}//Store block partial sum in global memoryif (index == 0)d_c[blockIdx.x] = partial_sum[0];
}
  • 每个块都有单独的一份共享内存副本,所以每个线程ID索引到的共享内存只能是当前块自己的那个副本

  • 当线线程总数小于元素数量的时候,它也会循环将tid 索引累加偏移到当前线程总数,继续索引下一对元素,并进行计算。每个线程得到的部分和结果被写入到共享内存。我们将继续使用共享内存上的这些线程的部分和计算出当前块的总体部分和

  • 在对共享内存中的数据读取之前,必须确保每个线程都已经完成了对共享内存的写入,可以通过 __syncthreads() 同步函数做到这一点

  • 计算当前块部分和的方法:

    • 1.让一个线程串行循环将这些所有的线程的部分和进行累加
    • 2.并行化:每个线程累加2个数的操作,并将每个线程的得到的1个结果覆盖写入这两个数中第一个数的位置,因为每个线程都累加了2个数,因此可以在第一个数中完成操作(此时第一个数就是两个数的和),后边对剩余的部分重复这个过程,类似将所有数对半分组相加吧,一组两个数,加完算出的新的结果作为新的被加数
    • 上述并行化的方法是通过条件为(i!=0)while循环进行的,后边的计算类似二分法,重复计算中间值与下一值的和,知道总数为0
  • main函数如下:

int main(void) 
{//Declare Host Arrayfloat *h_a, *h_b, h_c, *partial_sum;//Declare device Arrayfloat *d_a, *d_b, *d_partial_sum;//Calculate total number of blocks per gridint block_calc = (N + threadsPerBlock - 1) / threadsPerBlock;int blocksPerGrid = (32 < block_calc ? 32 : block_calc);// allocate memory on the host sideh_a = (float*)malloc(N * sizeof(float));h_b = (float*)malloc(N * sizeof(float));partial_sum = (float*)malloc(blocksPerGrid * sizeof(float));// allocate the memory on the devicecudaMalloc((void**)&d_a, N * sizeof(float));cudaMalloc((void**)&d_b, N * sizeof(float));cudaMalloc((void**)&d_partial_sum, blocksPerGrid * sizeof(float));// fill the host array with datafor (int i = 0; i<N; i++) {h_a[i] = i;h_b[i] = 2;}cudaMemcpy(d_a, h_a, N * sizeof(float), cudaMemcpyHostToDevice);cudaMemcpy(d_b, h_b, N * sizeof(float), cudaMemcpyHostToDevice);//Call kernel gpu_dot << <blocksPerGrid, threadsPerBlock >> >(d_a, d_b, d_partial_sum);// copy the array back to host memorycudaMemcpy(partial_sum, d_partial_sum, blocksPerGrid * sizeof(float), cudaMemcpyDeviceToHost);// Calculate final dot product on hosth_c = 0;for (int i = 0; i<blocksPerGrid; i++) {h_c += partial_sum[i];}printf("The computed dot product is: %f\n", h_c);
}
  • 在main函数中添加如下代码,检查该点乘结果是否正确:
#define cpu_sum(x) (x*(x+1))if (h_c == cpu_sum((float)(N - 1))){printf("The dot product computed by GPU is correct\n");}else{printf("Error in dot product computation");}// free memory on host and devicecudaFree(d_a);cudaFree(d_b);cudaFree(d_partial_sum);free(h_a);free(h_b);free(partial_sum);

在这里插入图片描述

矩阵乘法

  • 矩阵乘法A*B,A的行数需等于B的列数,将A的某行与B的所有的列进行点乘,然后对A的每一行以此类推
  • 下面将给出不使用共享内存和使用共享内存的内核函数来计算矩阵乘法
  • 先给出不使用共享内存的内核:
//Matrix multiplication using shared and non shared kernal
#include "stdio.h"
#include<iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#define TILE_SIZE 2//Matrix multiplication using non shared kernel
__global__ void gpu_Matrix_Mul_nonshared(float* d_a, float* d_b, float* d_c, const int size)
{int row, col;col = TILE_SIZE * blockIdx.x + threadIdx.x;row = TILE_SIZE * blockIdx.y + threadIdx.y;for (int k = 0; k < size; k++){d_c[row * size + col] += d_a[row * size + k] * d_b[k * size + col];}
}
  • 每个元素的线性索引可以这样计算:用它的行号乘以矩阵的宽度,再加上它的列号即可
  • 使用共享内存的内核函数如下:
// Matrix multiplication using shared kernel
__global__ void gpu_Matrix_Mul_shared(float *d_a, float *d_b, float *d_c, const int size)
{int row, col;//Defining Shared Memory,共享内存数量=块数__shared__ float shared_a[TILE_SIZE][TILE_SIZE];__shared__ float shared_b[TILE_SIZE][TILE_SIZE];col = TILE_SIZE * blockIdx.x + threadIdx.x;row = TILE_SIZE * blockIdx.y + threadIdx.y;for (int i = 0; i< size / TILE_SIZE; i++) {shared_a[threadIdx.y][threadIdx.x] = d_a[row* size + (i*TILE_SIZE + threadIdx.x)];shared_b[threadIdx.y][threadIdx.x] = d_b[(i*TILE_SIZE + threadIdx.y) * size + col];__syncthreads(); for (int j = 0; j<TILE_SIZE; j++)d_c[row*size + col] += shared_a[threadIdx.y][j] * shared_b[j][threadIdx.x];__syncthreads(); }
}
  • 主函数代码如下:
int main()
{const int size = 4;//Define Host Arrayfloat h_a[size][size], h_b[size][size],h_result[size][size];//Defining device Arrayfloat *d_a, *d_b, *d_result; //Initialize host Arrayfor (int i = 0; i<size; i++){for (int j = 0; j<size; j++){h_a[i][j] = i;h_b[i][j] = j;}}cudaMalloc((void **)&d_a, size*size*sizeof(int));cudaMalloc((void **)&d_b, size*size * sizeof(int));cudaMalloc((void **)&d_result, size*size* sizeof(int));//copy host array to device arraycudaMemcpy(d_a, h_a, size*size* sizeof(int), cudaMemcpyHostToDevice);cudaMemcpy(d_b, h_b, size*size* sizeof(int), cudaMemcpyHostToDevice);//Define grid and block dimensionsdim3 dimGrid(size / TILE_SIZE, size / TILE_SIZE, 1);dim3 dimBlock(TILE_SIZE, TILE_SIZE, 1);//gpu_Matrix_Mul_nonshared << <dimGrid, dimBlock >> > (d_a, d_b, d_result, size);gpu_Matrix_Mul_shared << <dimGrid, dimBlock >> > (d_a, d_b, d_result, size);cudaMemcpy(h_result, d_result, size*size * sizeof(int),	cudaMemcpyDeviceToHost);printf("The result of Matrix multiplication is: \n");for (int i = 0; i< size; i++){for (int j = 0; j < size; j++){printf("%f   ", h_result[i][j]);}printf("\n");}cudaFree(d_a);cudaFree(d_b);cudaFree(d_result);return 0;
}

在这里插入图片描述

  • ——————END——————

相关文章:

[8] CUDA之向量点乘和矩阵乘法

CUDA之向量点乘和矩阵乘法 计算类似矩阵乘法的数学运算 1. 向量点乘 两个向量点乘运算定义如下&#xff1a; #真正的向量可能很长&#xff0c;两个向量里边可能有多个元素 (X1,Y1,Z1) * (Y1,Y2,Y3) X1Y1 X2Y2 X3Y3这种原始输入是两个数组而输出却缩减为一个(单一值)的运…...

音视频开发9 FFmpeg 解复用框架说明,重要知识点

一&#xff0c;播放器框架 二 常用音视频术语 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a; 即特定格式的多媒体文件&#xff0c; 比如mp4、flv、mkv等。 媒体流&#xff08;Stream&#xff09;&#xff1a; 表示时间轴上的一段连续数据&#xff0…...

抖音小店出单之后怎么发货?抖店详细发货流程来了

大家好&#xff0c;我是喷火龙。 抖音小店发货是有规则的&#xff0c;如果出现超时发货或者虚假发货都会被平台处罚的&#xff0c;会影响我们店铺的评分和正常运营&#xff0c;还有些小伙伴们在发货的时候会遇到平台的违规提醒等问题。 今天我就给大家讲一下抖音小店的发货流…...

Transformer详解(5)-编码器和解码器

1、Transformer编码器 import torch from torch import nn import copy from norm import Norm from multi_head_attention import MultiHeadAttention from feed_forward import FeedForward from pos_encoder import PositionalEncoderdef get_clones(module, N):"&quo…...

线程安全-3 JMM

一.谈一下JMM 1.JMM&#xff0c;JavaMemoryModel&#xff0c;Java内存模型。定义了多线程对共享内存读写操作的行为规范&#xff0c;通过规范多线程对共享内存的读写操作&#xff0c;以保证指令执行和结果的正确性。 2.JMM把内存分为两块 &#xff08;1&#xff09;主内存&a…...

4 CSS的 变换、过渡与动画

CSS3引入了变换、过渡和动画特性&#xff0c;使得网页可以呈现出丰富的视觉效果和交互体验。通过这些新特性&#xff0c;开发者可以创建复杂的动画效果&#xff0c;而不需要使用JavaScript。 4.1 变换&#xff08;Transforms&#xff09; 变换允许开发者对元素进行旋转、缩放…...

前端基础入门三大核心之JS篇:掌握数字魔法 ——「累加器与累乘器」的奥秘籍【含样例代码】

前端基础入门三大核心之JS篇&#xff1a;掌握数字魔法 ——「累加器与累乘器」的奥秘籍 &#x1f9d9;‍♂️ 基础概念&#xff1a;数字的魔杖与炼金术累加器&#xff08;Accumulator&#xff09;累乘器&#xff08;Multiplier&#xff09; &#x1f4da; 实战演练&#xff1a;…...

git clone 出现的问题

问题: core源码ref新API % git clone https://github.com/xxxx.git Cloning into core... remote: Enumerating objects: 58033, done. remote: Counting objects: 100% (1393/1393), done. remote: Compressing objects: 100% (750/750), done. error: 432 bytes of body are …...

Vue2和Vue3生命周期的对比

Vue2和Vue3生命周期的对比 Vue2 和 Vue3 生命周期对照表Vue2 和 Vue3 生命周期图示 Vue2 和 Vue3 生命周期对照表 触发时机Vue2.xVue3.x组件创建时运行beforeCreate setup createdsetup 挂载在DOM时运行beforeMountonBeforeMountmountedonMounted响应数据修改时运行beforeUpdat…...

全面解析Java.lang.ClassCastException异常

全面解析Java.lang.ClassCastException异常 全面解析Java.lang.ClassCastException异常&#xff1a;解决方案与最佳实践 &#x1f680;&#x1f4da;摘要引言1. 什么是Java.lang.ClassCastException&#xff1f;代码示例 2. 报错原因2.1 类型不兼容2.2 泛型类型擦除2.3 接口和实…...

美团Java社招面试题真题,最新面试题

如何处理Java中的内存泄露&#xff1f; 1、识别泄露&#xff1a; 使用内存分析工具&#xff08;如Eclipse Memory Analyzer Tool、VisualVM&#xff09;来识别内存泄露的源头。 2、代码审查&#xff1a; 定期进行代码审查&#xff0c;关注静态集合类属性和监听器注册等常见内…...

二十八、openlayers官网示例Data Tiles解析——自定义绘制DataTile源数据

官网demo地址&#xff1a; https://openlayers.org/en/latest/examples/data-tiles.html 这篇示例讲解的是自定义加载DataTile源格式的数据。 先来看一下什么是DataTile&#xff0c;这个源是一个数组&#xff0c;与我们之前XYZ切片源有所不同。DataTile主要适用于需要动态生成…...

分布式事务解决方案(最终一致性【TCC解决方案】)

最终一致性分布式事务概述 强一致性分布式事务解决方案要求参与事务的各个节点的数据时刻保持一致&#xff0c;查询任意节点的数据都能得到最新的数据结果&#xff0c;这就导致在分布式场景&#xff0c;尤其是高并发场景下&#xff0c;系统的性能受到了影响。而最终一致性分布式…...

App Inventor 2 Encrypt.Security 安全性扩展:MD5哈希,SHA/AES/RSA/BASE64

这是关于App Inventor和Thunkable安全性的扩展&#xff0c;它提供MD5哈希&#xff0c;SHA1和SHA256哈希&#xff0c;AES加密/解密&#xff0c;RSA加密/解密&#xff0c;BASE64编码/解码方法。 权限 此扩展程序不需要任何权限。 事件 OnErrorOccured 抛出任何异常时将触发此事件…...

深入了解Linux中的环境变量

在Linux系统中&#xff0c;环境变量&#xff08;Environment Variables&#xff09;是用于配置操作系统和应用程序运行环境的一种机制。它们储存在键值对中&#xff0c;可以控制程序的行为、路径查找和系统配置。本文将深入探讨环境变量的基本概念、常见类型、设置和管理方法&a…...

雷军-2022.8小米创业思考-8-和用户交朋友,非粉丝经济;性价比是最大的诚意;新媒体,直播离用户更近;用真诚打动朋友,脸皮厚点!

第八章 和用户交朋友 2005年&#xff0c;为了进一步推动金山的互联网转型&#xff0c;让金山的同事更好地理解互联网的精髓&#xff0c;我推动了一场向谷歌学习的运动&#xff0c;其中一个小要求就是要能背诵“谷歌十诫”。 十诫的第一条就令人印象深刻&#xff1a;以用户为中…...

【Vue2.x】props技术详解

1.什么是prop&#xff1f; 定义&#xff1a;组件标签上注册的一些自定义属性作用&#xff1a;向子组件传递数据特点 可以传递任意数量的prop可以传递任意类型的prop 2.prop校验 为了避免乱传数据&#xff0c;需要进行校验 完整写法 将之前props数组的写法&#xff0c;改为对象…...

C语言例题46、根据公式π/4=1-1/3+1/5-1/7+1/9-1/11+…,计算π的近似值,当最后一项的绝对值小于0.000001为止

#include <stdio.h> #include <math.h>int main() {int fm 1;//分母double sign 1;//正负号double fzs 1;//分子式double sum 0;while (fabs(fzs) > 0.000001) {sum fzs;sign * -1; //变换正负号fm 2; //分母3、5、7、9...增长fzs sign / fm;//分子式…...

fpga系列 HDL: 05 阻塞赋值(=)与非阻塞赋值(<=)

在Verilog硬件描述语言&#xff08;HDL&#xff09;中&#xff0c;信号的赋值方式主要分为两种&#xff1a;连续赋值和过程赋值。每种赋值方式有其独特的用途和语法&#xff0c;并适用于不同类型的电路描述。 1. 连续赋值&#xff08;Continuous Assignment,assign 和&#xf…...

大白话DC3算法

DC3算法是什么 DC3算法&#xff08;也称为Skew算法&#xff09;是一种高效的构建后缀数组的算法&#xff0c;全称为Difference Cover Modulo 3算法。 该算法于2002年被提出&#xff0c;论文参考&#xff1a; https://www.cs.cmu.edu/~guyb/paralg/papers/KarkkainenSanders0…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 原创笔记&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;《数据结构第4章 数组和广义表》…...