【CUDA】 矩阵乘向量 matVecMul

Matrix - Vector Multiplication

矩阵-向量乘法是线性代数中的基本操作。它用于将一个矩阵与一个向量相乘。乘法的结果是与输入向量大小相同的向量。

矩阵和向量的乘法如图1所示。

图1

基础kernel与共享内存kernel

执行矩阵-向量乘法的基础kernel是使用单个线程执行输出向量的单个元素的乘法。这意味着所需的线程数等于输出向量中的元素数。线程以一维网格排列,每个线程被分配一个唯一的索引。线程的索引用于访问输入矩阵的对应行。对行和向量进行乘法操作,结果存储在输出向量的对应元素中。

在这种基础kernel中,每个线程将加载整个输入向量。这并不高效,因为输入向量被多次加载。为了避免这种情况,我们可以使用一个tile将输入向量存储在共享内存中。tile是一个大小等于块中线程数的一维数组。向量被加载到tile中,每个线程使用tile执行乘法。

Code

Host代码初始化输入矩阵和向量,随机赋值并调用kernel执行乘法。输入矩阵以线性化格式存储。

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cmath>#include <cuda_runtime.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include "image2gray.cuh"
#include "helper_cuda.h"
#include "error.cuh"const int FORTIME = 50;int main(int argc, char* argv[])
{int rows, cols, t_x;rows = 4096;cols = 4096;t_x = 1024;thrust::host_vector<float> h_in_mat(rows * cols);thrust::host_vector<float> h_in_vec(cols);srand(time(NULL));for (int i = 0; i < rows * cols; i++) {h_in_mat[i] = rand() / (float)RAND_MAX;if (i < cols)h_in_vec[i] = rand() / (float)RAND_MAX;}thrust::host_vector<float> h_out(rows);for (int i = 0; i < rows; ++i)for (int j = 0; j < cols; ++j)h_out[i] += h_in_mat[i * cols + j] * h_in_vec[j];thrust::device_vector<float> d_in_mat = h_in_mat;thrust::device_vector<float> d_in_vec = h_in_vec;thrust::device_vector<float> d_out(rows);dim3 block(t_x);dim3 grid((rows + block.x - 1) / block.x);cudaEvent_t start, stop;checkCudaErrors(cudaEventCreate(&start));checkCudaErrors(cudaEventCreate(&stop));checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)mat_vec_mul << <grid, block >> > (thrust::raw_pointer_cast(d_out.data()),thrust::raw_pointer_cast(d_in_mat.data()),thrust::raw_pointer_cast(d_in_vec.data()),rows, cols);checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));float elapsed_time;checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "elapsed_time:" << elapsed_time / FORTIME << std::endl;bool success = true;for (int i = 0; i < rows; ++i)if (abs(h_out[i] - d_out[i]) >= 0.001) {std::cout << "Error at " << i << ": " << h_out[i] << " != " << d_out[i] << " (Mat Vec Mul)" << std::endl;success = false;break;}if (success)std::cout << "Success (Mat Vec Mul)" << std::endl;checkCudaErrors(cudaEventRecord(start));for (int i = 0; i < FORTIME; i++)mat_vec_mul_tiles << <grid, block, block.x * sizeof(float) >> > (thrust::raw_pointer_cast(d_out.data()),thrust::raw_pointer_cast(d_in_mat.data()),thrust::raw_pointer_cast(d_in_vec.data()),rows, cols);checkCudaErrors(cudaEventRecord(stop));checkCudaErrors(cudaEventSynchronize(stop));checkCudaErrors(cudaEventElapsedTime(&elapsed_time, start, stop));std::cout << "elapsed_time:" << elapsed_time / FORTIME << std::endl;success = true;for (int i = 0; i < rows; ++i)if (abs(h_out[i] - d_out[i]) >= 0.001) {std::cout << "Error at " << i << ": " << h_out[i] << " != " << d_out[i] << " (Mat Vec Mul Tiles)" << std::endl;success = false;break;}if (success)std::cout << "Success (Mat Vec Mul Tiles)" << std::endl;return 0;
}

Note:

helper_cuda.h 与error.cuh头文件为错误查验工具。后续会发布到Github。

去除checkCudaErrors等错误查验函数不影响程序运行。


下面显示了两个kernel。

template<typename T> __global__
void mat_vec_mul(T *out, T *in_mat, T *in_vec, int m, int n) {int i = blockIdx.x * blockDim.x + threadIdx.x;if(i >= m) return;T temp = 0;for (int k = 0; k < n; ++k)temp += in_mat[i * n + k] * in_vec[k];out[i] = temp;
}template<typename T> __global__
void mat_vec_mul_tiles(T *out, T *in_mat, T *in_vec, int m, int n) {extern __shared__ uint8_t tiles_uint8[];T *tiles = reinterpret_cast<T*>(tiles_uint8);int i = blockIdx.x * blockDim.x + threadIdx.x;T res = 0;int n_phases = (n + blockDim.x - 1) / blockDim.x;for (int phase = 0; phase < n_phases; ++phase){int elem_idx = phase * blockDim.x + threadIdx.x;tiles[threadIdx.x] = elem_idx < n ? in_vec[elem_idx] : 0;__syncthreads();if(i < m)for (int k = 0; k < blockDim.x && phase * blockDim.x + k < n; ++k)res += in_mat[i * n + phase * blockDim.x + k] * tiles[k];__syncthreads();}out[i] = res;
}

基础kernel

kernel首先计算线程的索引。如果索引在输出向量的范围内,则执行乘法。

int i = blockIdx.x * blockDim.x + threadIdx.x;
if(i >= m) return;

将矩阵的每一列与向量相应元素相乘,并将结果存储在输出向量的相应元素中。

T temp = 0;
for (int k = 0; k < n; ++k)temp += in_mat[i * n + k] * in_vec[k];
out[i] = temp;

共享内存kernel

kernel首先计算线程的索引。kernel不会检查边界条件,因为需要加载向量到tile中的超出输出向量范围的线程。

int i = blockIdx.x * blockDim.x + threadIdx.x;

现在,乘法被分成几个阶段。在每个阶段,线程将把向量的单个或零个元素(如果它们超出范围)加载到tile中。然后线程将执行加载到tile中的向量和输入矩阵行的相应元素的乘法。结果存储在输出向量的相应元素中。

在加载向量到tile中和执行乘法时,线程都会执行边界检查,以避免超出范围的访问。

T res = 0;
int n_phases = (n + blockDim.x - 1) / blockDim.x;
for (int phase = 0; phase < n_phases; ++phase){int elem_idx = phase * blockDim.x + threadIdx.x;tiles[threadIdx.x] = elem_idx < n ? in_vec[elem_idx] : 0;__syncthreads();if(i < m)for (int k = 0; k < blockDim.x && phase * blockDim.x + k < n; ++k)res += in_mat[i * n + phase * blockDim.x + k] * tiles[k];__syncthreads();
}
out[i] = res;

性能分析

运行时间:

矩阵维度:4096 × 4096

向量维度:4096

线程块维度:1024

由运行时间分析,引入共享内存效果比较明显,而且经测试输入向量维度越大,共享内存效果越明显。

Note:单次运行可能因为设备启动原因,各种算法运行时间差异较大,可采用循环20次以上取平均值。

笔者采用设备:RTX3060 6GB

PMPP项目提供的分析

kernel的性能是使用NvBench项目在多个gpu中测量的。研究的性能测量方法有:

内存带宽:每秒传输的数据量。

内存带宽利用率:占用内存带宽的百分比。

基础kernel

共享内存kernel

参考文献:

1、大规模并行处理器编程实战(第2版)

2、​​​PPMP

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

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

相关文章

【SOLID原则前端中的应用】开闭原则(Open/Closed Principle)- vue3示例

开闭原则&#xff08;Open/Closed Principle&#xff09;在Vue 3中的应用 开闭原则&#xff08;Open/Closed Principle&#xff0c;OCP&#xff09;规定&#xff0c;软件实体&#xff08;类、模块、函数等&#xff09;应该对扩展开放&#xff0c;对修改关闭。 也就是说&#xf…

大型网站软件系统架构演进过程

在我们的生活中,通常会使用大型网站系统,比如购物网站淘宝,京东,阿里1688;大型搜索引擎网站百度,社交类的如腾讯旗下的微信,QQ及新浪旗下的微博等,他们通常都有一下特点: 高并发、大流量&#xff1a;这些系统必须能够处理成千上万甚至数百万的并发用户请求&#xff0c;以及持续…

Dubbo内部通信流程

我当时在学习的过程中搭建过demo&#xff0c;具体流程就是&#xff0c;我先定义了一个api接口模块&#xff0c;还定义一个服务提供者模块&#xff0c;然后服务提供方实现该接口&#xff0c;定义该方法具体的实现impl类&#xff0c;服务提供方启动时&#xff0c;将要暴露的服务和…

运维行业的全新视界:一体化监控解决方案

在当今的IT环境中&#xff0c;运维团队面临着前所未有的挑战。随着技术的快速发展&#xff0c;企业需要更加高效、智能的监控工具来确保IT基础设施的稳定运行。本文旨在为运维团队提供一个全面的、集成多种功能的监控解决方案参考。 一、总体架构与要求 首先&#xff0c;一个理…

Python中的模块和包定义以及如何在Python中导入和使用它们

在Python中&#xff0c;模块&#xff08;Module&#xff09;和包&#xff08;Package&#xff09;是组织代码、重用代码的基本单位&#xff0c;它们让Python的编程更加模块化&#xff0c;易于管理和维护。 模块&#xff08;Module&#xff09; 模块是一个包含Python定义和声明…

金融科技的移动时代:探索APP与SaaS系统的结合之道

金融科技&#xff08;FinTech&#xff09;的革新正在重塑金融服务行业的未来。移动应用&#xff08;APP&#xff09;与软件即服务&#xff08;SaaS&#xff09;系统的结合&#xff0c;为金融行业带来了前所未有的灵活性和可扩展性。 一、金融科技的发展趋势 金融科技通过技术…

在线快速制作二维码的技巧,支持生成多种内容二维码

现在用二维码来分享内容是很多场景下会使用的一种方式&#xff0c;常见的展示内容有图片、文件、文本、音频、视频等&#xff0c;都可以生成二维码之后。通过手机扫码来查看内容&#xff0c;有利于内容的快速传播&#xff0c;并且用户获取信息也更加的方便。 下面来教大家使用…

ubuntu rc.local开机自启动

https://blog.csdn.net/qq_48974566/article/details/137212295

Web应用防火墙用在哪些场景?

WAF是Web Application Firewall的缩写&#xff0c;翻译为“Web应用防火墙”是一种网络安全设备或服务&#xff0c;用于保护Web应用程序免受各种网络攻击和漏洞的影响。 WAF特别设计用于识别和阻止特定于Web应用程序的攻击&#xff0c;例如SQL注入、跨站脚本(XSS)、跨站请求伪造…

力扣习题--哈沙德数

一、前言 本系列主要讲解和分析力扣习题&#xff0c;所以的习题均来自于力扣官网题库 - 力扣 (LeetCode) 全球极客挚爱的技术成长平台 二、哈沙德数 1. 哈沙德数 如果一个整数能够被其各个数位上的数字之和整除&#xff0c;则称之为 哈沙德数&#xff08;Harshad number&…

CTF常用sql注入(二)报错注入(普通以及双查询)

0x05 报错注入 适用于页面无正常回显&#xff0c;但是有报错&#xff0c;那么就可以使用报错注入 基础函数 floor() 向下取整函数 返回小于或等于传入参数的最大整数。换句话说&#xff0c;它将数字向下取整到最接近的整数值。 示例&#xff1a; floor(3.7) 返回 3 floor(-2…

5.基于SpringBoot的SSMP整合案例-数据层开发

目录 1.新建项目 2.实体类开发&#xff1a; 2.1在pom.xml中增加Lombok坐标&#xff1a; 2.2添加Book实体类 3.数据层开发&#xff1a; 3.1 配置MyBatisPlus与Druid 3.2创建数据层接口 3.3写测试类 3.4点击运行&#xff1a; 4.数据层快速开发&#xff1a; 4.1配置MyB…

流式数据库 RisingWave「Demo」:直播指标实时分析

直播因其能与观众进行实时互动的独特优势&#xff0c;成为目前最为流行的娱乐形式之一。想要优化直播效果&#xff0c;有许多指标需要跟踪。最常见的指标比如&#xff1a;人流量、评论数量、直播卡顿时长等等。 本教程将分享如何使用 RisingWave 监控直播流量指标。我们为本教…

python访问mongoDB

pip install pymongo1.建立连接 &#xff08;1&#xff09;模块引用 import pymongoClientMongoClient(host10.90.9.101,port27017)(2)访问数据库 dbclient.myDBdbconn.get_database("myDB")2.集合操作 &#xff08;1&#xff09;插入文档 colldb.get_collection(…

docker-compose version is obsolete

如果更新了docker或者docker-desktop 启动时候发现有 version is obsolete警告 删除yml第一行的version字段 上社区原文 Technically you can still define it… But you will get the warning you observed, and it won’t be used, as docker compose v2 (the cli plugin…

关于TaOTUB1的信息总结

关于TaOTUB1的信息总结 文献标题: Reducing expression of TaOTUB1s decreases tiller number in wheat 核心内容: 背景和目的: 小麦的分蘖数量是影响产量的重要农艺性状。OTUB1在水稻中被发现能够调控分蘖数量&#xff0c;而在小麦中的功能尚不明确。本研究旨在通过鉴定和分…

C++视觉开发 四.手势识别

本章记录传统手势识别&#xff0c;在表示0-5六个数值时的识别问题。例如识别剪刀石头布&#xff0c;手势&#xff0c;以及其表示的动作。在识别时将手势中的凹陷区域称为凸缺陷&#xff0c;其个数作为识别的重要依据。 需要注意&#xff0c;在凸缺陷个数为0时&#xff0c;无法…

细说QT程序高分屏适配

文章目录 高分屏适配适配屏幕缩放禁用屏幕缩放开启系统配置属性获取当前屏幕信息在Qt中实现高分辨率屏幕的适配,主要涉及对界面元素、字体大小和布局进行优化,以保证应用程序在不同分辨率和设备像素比率(DPI)的显示屏上均能正确显示。下面是一些关键步骤和技巧,用于在Qt应用…

S272钡铼技术4G无线RTU支持多路DIN输入和模拟量转换至4G网络

钡铼第四代RTU S272是一款先进的工业级4G远程遥测终端&#xff0c;为各种远程工业数据采集和控制系统提供了高效解决方案。结合了现代通信技术和多功能的输入输出接口&#xff0c;S272不仅支持多路数字量和模拟量输入&#xff0c;还具备灵活的扩展性和强大的控制功能&#xff0…

简单实现利用飞书机器人推送消息给相关人员群的方法

利用的飞书的自定义机器人&#xff0c;将系统中的错误信息推送给技术群的功能。 飞书文档地址&#xff1a;开发文档 - 飞书开放平台 自定义机器人只能在群聊中使用的机器人&#xff0c;在当前的群聊中通过调用webhook地址来实现消息的推送。 配置逻辑可以看飞书的官方文档&am…