CPU/GPU实现向量内积

news2024/11/24 16:36:04

向量内积(点乘/点积/数量积):两个向量对应元素相乘之后求和:

CPU实现: 

//cpu 实现一下向量内积

#include<stdio.h>
template<typedef T>
void dot_mul(T *a, T *b, T *c, int n)
{   
    double tmp = 0;
    for(int i = 0; i < n; i++)
    {
        tmp += a[i] * b[i];
    }
    *c = tmp;
}

int main()
{
    //定义数组以及数组的大小
    float a[N], b[N];
    float c = 0;
    for(int i = 0; i < N; ++i)
    {
        a[i] = i * 1.0;
        b[i] = 1.0
    }
    dot_cpu(a, b, &c, N);
    printf("a dot b output %f\n", c);
    printf("Hello World!\n");

首次接触bank conflict的概念,在这里补充一下。

bank是shared memory中用来存储数据的特殊组织方式。为了高效存取输入shared memory分为32个存储体(bank), 对应32个线程。每个bank有一个固定的带宽,可以同时服务一个线程的访问。当多个线程在一个时钟周期内访问一个bank的不同地址时,会产生bank conflict。因为bank的读取带宽不能高效的同时服务多个线程,因此需要需要解决bank conflict。有个不解的地方,是什么导致所有的thread都去访问同一个bank?

了解一个bank的属性:bank的宽度是指bank存储器的位宽。位宽是存储器连接的总线一次可以传数的数据量,可以是32bit,也可以是64bit,取决于总线的位数。可以是4字节/8字节。

避免bank conflict的方法有以下几种:

  • 使用不同的bank size,可以通过cudaDeviceSetSharedMemConfig函数来设置bank size为4字节或8字节,这样可以改变shared memory到bank的映射方式,减少冲突的可能性。
  • 使用memory padding,即在shared memory的数组中增加一些空白的元素,使得不同的线程访问不同的bank,从而避免冲突。
  • 使用不同的访问模式,比如使用转置或者重排的方式,使得一个warp中的线程访问不同的bank或者同一个地址,从而避免冲突。

回归正题,用cuda实现向量的内积(点积/点乘/数量积)。

单block分散归约法

 

第一次理解单block分散归约 takes 3 hours!

第一次手撸整理单block分散归约代码 takes one and a half hours!

main 函数 takes half an hour !

以下是我学习整理后的kernel函数:

#include "cuda_runtime.h"
#include "stdio.h"

#define threadnums 32
#define N 2048

//单block分散归约法
template <typedef T>
__global__ dotmul_gpu_1(T *a, T *b, T *c, int N)
{
    const int nThreadIdx = threadIdx.x;//当前线程ID索引
    const int nBlockDimX = blockDim.x;//一个block内开启的线程总数
    int nTid = nTreadIdx;
    dobule dTmp = 0.0;

    //开辟shared memory,大小与线程数量一致
    __shared__ T tmp[nBlockDimX];
    //step 1:
    //每个线程负责 N/nBlockDimX 个元素相乘后的累加 
    while(nTid < N)
    {
        dTmp += a[nTid] * b[nTid];
        nTid += nBlockDimX;
    }
    //每个线程将以上计算结果放入共享内存中
    tmp[nThreadIdx] = dTmp;

    //同步线程,等待所有线程完成以上计算
    __syncthreads();

    //step2:归约reduction
    int i = 2;
    int j = 1;
    while(i <= nBlockDimX)
    {
        if(nThreadIdx / i == 0)
        {
            //所有线程完成一次求和归约计算
            dTmp = tmp[nThreadIdx] + tmp[nThreadIdx + j];
            tmp[nThreadIdx] = dTmp;

        }

        __syncthreads();

        //这个地方利用i和j进行索引比较巧妙
        //32个线程进行求和归约,每次归约,线程索引的元素下标如下:
        //第一次归约:0+1, 2+3, 4+5, 6+7, 8+9, 10+11, 12+13,14+15, 16+17, 18+19, 20+21, 22+23, 24+25, 26+27, 28+29, 30+31
        //第二次归约:0+2, 4+6, 8+10, 12+14, 16+18, 20+22, 24+26, 28+30 
        //第三次归约:0+4, 8+12, 16+20, 24+28
        //第四次归约:0+8, 16+24
        //第五次归约:0+16
        //求和归约值:0
        i *= 2;
        j *= 2;
    }

    //此处只在一个线程中获取向量内积值,因此需要线程ID判断
    if(0 == nTreadIdx)
        *c = tmp[0];
}

 主函数整理如下:

int main()
{
    float a[N], b[N];
    //对向量a[], b[]初始化值
    for(int i = 0; i < N; i++)
    {
        a[i] = 1.0;
        b[i] = i * 1.0;
    }

    float *d_a=NULL, *d_b=NULL, *d_c=NULL;
    //将数组a[]的数据从CPU拷贝到GPU
     cudaMalloc(&d_a, N*sizeof(float));
     cudaMemcpyAsync(d_a, a, N*sizeof(float), cudaMemcpyHostToDevice);
    //将数组b[]的数据从CPU拷贝到GPU
     cudaMalloc(&d_b, N*sizeof(float));
     cudaMemcpyAsync(d_b, b, N*sizeof(float), cudaMemcpyHostToDevice);
     
     //不要忘记了结果也需要存储在显存上!
     cudaMalloc(&d_c, sizeof(float));

     //调用kernel函数
     dim3 blocks(1,0,0);
     dim3 threadPerBlock(threadnums, 0, 0);
     
     dotmul_gpu_1<<<blocks, threadPerBlock>>>(d_a, d_b, d_c, N);

     //分配的显存需要手动释放
     cudaFree(d_a);
     cudaFree(d_b);
     cudaFree(d_c);

    return 0;
}

 参考链接:

CUDA学习(十):向量内积的多种方法实现_为向量类增加计算内积的功能。-CSDN博客

拯救你的CUDA!什么是bank,为什么会发生bank conflict???_哔哩哔哩_bilibili

 该方法存在的问题在参考文章中被指出有违背访问对其原则、容易产生bank conflict。后面再一一学习补充。

此外,解决cuda上向量内积的方法还有,留待后续学习补充:

单Block低线程归约向量内积

多block向量内积

cublas库实现向量内积

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

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

相关文章

为什么同样是做测试,别人年薪30W+?我10k!!!

作为一名初出茅庐的软件测试员&#xff0c;职业发展的道路的确蜿蜒曲折&#xff0c;面对一次次的岗位竞争&#xff0c;挑战一道道的面试关卡&#xff0c;一边带着疑惑&#xff0c;一边又要做出选择&#xff0c;只能无奈的感叹&#xff1a;比你优秀的人比你还努力&#xff0c;你…

mysql主从搭建(docker)

一、主从概述 MySQL主从又叫Replication、AB复制。简单讲就是A与B两台机器做主从后&#xff0c;在A上写数据&#xff0c;另外一台B也会跟着写数据&#xff0c;实现数据实时同步。有这样几个关键点&#xff1a; 1&#xff09;MySQL主从是基于binlog&#xff0c;主上需开启binl…

“一键搜索,海量商品任你选!多平台聚合,购物更便捷!“

对于多平台聚合搜索&#xff0c;根据关键词取商品列表&#xff0c;您需要使用第三方服务或软件来实现。以下是一些可能的选择&#xff1a; 使用第三方聚合搜索工具&#xff1a;有些第三方工具可以聚合多个电商平台的商品数据&#xff0c;并提供统一的搜索接口。您可以使用这些…

机器人制作开源方案 | 钻孔植树一体化沙漠车

作者&#xff1a;徐邦国、张博宇、刘露、李晶晶、吕洁秀单位&#xff1a;天津职业技术师范大学 机械工程学院指导老师&#xff1a;何永利 摘要&#xff1a;本项目旨在设计一种专用于沙漠植树的植树车&#xff0c;以沙漠自动化植树为研究对象&#xff0c;提出一种创新式钻…

初识分布式键值对存储etcd

欢迎大家到我的博客浏览。胤凯 (oyto.github.io)大家好&#xff0c;今天我带大家来学习一下 etcd。 一、什么是 etcd etcd 是一个开源的分布式键值存储系统&#xff0c;主要用于构建分布式系统中那点服务发现、配置管理、分布式锁等场景。它采用 Raft 一致性算法来确保所有节…

Swagger(5):Swagger2常用注解

1 Api Api是类上注解。控制整个类生成接口信息的内容。 tags&#xff1a;类的名称。可以有多个值&#xff0c;多个值表示多个副本。description:描述&#xff0c;已过时。 注解如下&#xff1a; Api(tags {"MyController","Swagger学习控制器"}, desc…

详解SwinIR的论文和代码(SwinIR: Image Restoration Using Swin Transformer)

paper&#xff1a;https://arxiv.org/abs/2108.10257 code&#xff1a;https://github.com/JingyunLiang/SwinIR 目录 1. Swin Transformer layers1.1 局部注意力1.2 移动窗口机制1.3 关键代码理解 2. 整体网络结构2.1 浅层特征提取2.2 深层特征提取2.3 图像重建 3.总结 SwinI…

服务器探针-serverstatus

{alert type"info"} 之前给大家介绍过一个简单的服务器监控。uptime-kuma 今天给各位带来一个酷炫的多服务器探针和多服务器监控。ServerStatus {/alert} 作者的开源项目地址如下&#xff1a;https://github.com/cppla/ServerStatus 作者的项目体验地址如下 https://…

运动耳机什么牌子好?十大运动蓝牙耳机品牌排行榜

​运动耳机需求各有不同&#xff0c;对于我们每个人来说&#xff0c;选择最适合自己的耳机是一项重要任务。在这个耳机类型繁多&#xff0c;五花八门的时代&#xff0c;如何找到一款适合自己的运动耳机呢&#xff1f;选对运动耳机很重要&#xff0c;所以接下来安利五款相当不错…

element-ui中怎样使用iconfont的图标

1 登录 https://www.iconfont.cn/ 2 搜索合适的图 这里可以找到这个图所在的图库。这样就可以一次查找到对应的所有同款图标 3 选择同款加入购物车 4 将购物车的icon加入项目&#xff0c;注意是新建项目&#xff0c;除非你是确定需要前面已经加过的icon 5 下载icon 选择fon…

SpringBoot的启动流程

一、SpringBoot是什么&#xff1f; springboot是依赖于spring的&#xff0c;比起spring&#xff0c;除了拥有spring的全部功能以外&#xff0c;springboot无需繁琐的xml配置&#xff0c;这取决于它自身强大的自动装配功能&#xff1b;并且自身已嵌入Tomcat、Jetty等web容器&am…

课程设计:C++实现哈夫曼编码

功能实现&#xff1a; //1:先计算每个字符的权重//2&#xff1a;构建哈夫曼树//3&#xff1a;得出每个字符的哈夫曼编码。//4:根据哈夫曼编码转化为字符 代码实现&#xff1a; // 哈夫曼编码.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //1:先计…

vue动态配置路由

文章目录 前言定义项目页面格式一、vite 配置动态路由新建 /router/utils.ts引入 /router/utils.ts 二、webpack 配置动态路由总结如有启发&#xff0c;可点赞收藏哟~ 前言 项目中动态配置路由可以减少路由配置时间&#xff0c;并可减少配置路由出现的一些奇奇怪怪的问题 路由…

如何将文字、图片、视频、链接等内容生成一个二维码?

通过二维彩虹的【H5编辑】功能&#xff0c;就可以将文字、图片、视频、文件、链接等多种格式的内容编辑在一个页面&#xff0c;然后生成一个自定义的二维码——H5编辑二维码。扫描后&#xff0c;即可查看二维码中的详细图文视频等内容了。这个功能大受欢迎&#xff01; 这个H5…

深度学习之基于CT影像图像分割检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于CT影像的图像分割检测系统可以被设计成能够自动地检测出CT图像中的病变部位或解剖结构&#xff0c;以协助医生进…

庖丁解牛:NIO核心概念与机制详解 05 _ 文件锁定

文章目录 Pre概述锁定文件 &#xff08;lock&#xff09;Code文件锁定和可移植性 Pre 庖丁解牛&#xff1a;NIO核心概念与机制详解 01 庖丁解牛&#xff1a;NIO核心概念与机制详解 02 _ 缓冲区的细节实现 庖丁解牛&#xff1a;NIO核心概念与机制详解 03 _ 缓冲区分配、包装和…

calibre更新 环境变量设置

我这里是从别的地方copy过来的calibre&#xff0c;所以不用安装。 如果需要安装请参考&#xff1a; Caibre2022.3_17版本安装及遇到问题 - 梅希的日志 - EETOP 创芯网论坛 (原名&#xff1a;电子顶级开发网) -将copy过来的calibre放在原来calibre的位置。 打开工作路径下的.b…

【Vue】Vue3 超简单拖拽条动态修改容器宽度

demo 代码 const leftBoxWidth ref(200); // 默认宽度 const leftResize (e: MouseEvent) > {const startX e.clientX;const startWidth leftBoxWidth.value;const mouseMove (documentE: MouseEvent) > {// 80 是左侧菜单宽度leftBoxWidth.value startWidth docu…

不懂找伦敦银趋势?3个方法搞定

趋势是我们的朋友&#xff0c;但是这个朋友却很喜欢跟我们开玩笑&#xff0c;如果我们不留意&#xff0c;根本发觉不了它的存在。怎么找到趋势本体并且和它做个好朋友呢&#xff1f;下面我们就来介绍三个方法。 数波段的高点和低点。我们以当前的市场波动价格为轴&#xff0c;向…

快手运营的必备的10个工具

一、引言 快手作为短视频领域的佼佼者&#xff0c;为众多创作者提供了广阔的舞台。要想在快手运营中取得成功&#xff0c;掌握一些必备的工具是必不可少的。本文将为您介绍快手运营的10个必备工具&#xff0c;帮助您提高工作效率&#xff0c;优化内容创作。 二、工具推荐 1. …