CUDA编程 - 用向量化访存优化 - Cuda elementwise - Add(逐点相加)- 学习记录

Cuda elementwise - Add

  • 一、简介
    • 1.1、ElementWise Add
    • 1.2、 float4 - 向量化访存
  • 二、实践
    • 2.1、如何使用向量化访存
    • 2.1、简单的逐点相加核函数
    • 2.2、ElementWise Add + float4(向量化访存)
    • 2.3、完整代码

一、简介

1.1、ElementWise Add

Element-wise 操作是最基础,最简单的一种核函数的类型,它的计算特点很符合GPU的工作方式:对于每个元素单独做一个算术操作,然后直接输出。

Add 函数 :逐点相加

  • 传入 数组 a,b,c
  • 传入 数据数量 N
  • 传出结果 数组c

1.2、 float4 - 向量化访存

所谓向量化访存,就是一次性读 4 个 float,而不是单单 1 个

要点:

  • 小数据规模情况下,可以不考虑向量化访存的优化方式
  • 大规模数据情况下,考虑使用向量化访存,且最好是缩小grid的维度为原来的1/4,避免影响Occupancy
  • float4 向量化访存只对数据规模大的时候有加速效果,数据规模小的时候没有加速效果

float4的性能提升主要在于访存指令减少了(同样的数据规模,以前需要4条指令,现在只需1/4的指令),指令cache里就能存下更多指令,提高指令cache的命中率。

判断是否用上了向量化访存,是在 nsight compute 看生成的SASS代码里会有没有LDG.E.128 Rx, [Rx.64]或STG.E.128 [R6.64], Rx这些指令的存在。有则向量化成功,没有则向量化失败。

在这里插入图片描述

官方参考链接1
官方参考链接2

二、实践

2.1、如何使用向量化访存

c :

#define FLOAT4(value)  *(float4*)(&(value))

宏解释:

对于一个值,先对他取地址,然后再把这个地址解释成 float4
对于这个 float4的指针,对它再取一个值
这样编译器就可以一次读四个 float

c++ :

#define FLOAT4(value) (reinterpret_cast<float4*>(&(value))[0])

2.1、简单的逐点相加核函数

__global__ void elementwise_add(float* a, float* b, float* c, int N) {
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  if (idx < N) c[idx] = a[idx] + b[idx];
}

2.2、ElementWise Add + float4(向量化访存)

__global__ void elementwise_add_float4(float* a, float* b, float *c, int N)
{
    int idx = (blockDim.x * blockIdx.x + threadIdx.x) * 4;

    if(idx < N ){
        float4 tmp_a = FLOAT4(a[idx]);
        float4 tmp_b = FLOAT4(b[idx]);
        float4 tmp_c;
        tmp_c.x = tmp_a.x + tmp_b.x;
        tmp_c.y = tmp_a.y + tmp_b.y;
        tmp_c.z = tmp_a.z + tmp_b.z;
        tmp_c.w = tmp_a.w + tmp_b.w;
        FLOAT4(c[idx]) = tmp_c;
    }
}

将核函数写成 float4 的形式的时候,首先要先使用宏定义(参考1.3),其次要注意线程数的变化。

线程数变化原因:因为一个线程可以处理4个float了,所以要减少 四倍的线程。

2.3、完整代码

elementwise_add.cu

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <vector>
#include<assert.h>
#include <algorithm>
#include <cublas_v2.h>
#include <cuda_runtime.h>

#define FLOAT4(value)  *(float4*)(&(value))

#define checkCudaErrors(func)               \
{                                   \
    cudaError_t e = (func);         \
    if(e != cudaSuccess)                                        \
        printf ("%s %d CUDA: %s\n", __FILE__,  __LINE__, cudaGetErrorString(e));        \
}

// ElementWise Add  
// elementwise_add<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, d_C, N);
// a: Nx1, b: Nx1, c: Nx1, c = elementwise_add(a, b)
__global__ void elementwise_add(float* a, float* b, float* c, int N) {
  int idx = blockIdx.x * blockDim.x + threadIdx.x;
  if (idx < N) c[idx] = a[idx] + b[idx];
}

__global__ void elementwise_add_float4(float* a, float* b, float *c, int N)
{
    int idx = (blockDim.x * blockIdx.x + threadIdx.x) * 4;

    if(idx < N ){
        float4 tmp_a = FLOAT4(a[idx]);
        float4 tmp_b = FLOAT4(b[idx]);
        float4 tmp_c;
        tmp_c.x = tmp_a.x + tmp_b.x;
        tmp_c.y = tmp_a.y + tmp_b.y;
        tmp_c.z = tmp_a.z + tmp_b.z;
        tmp_c.w = tmp_a.w + tmp_b.w;
        FLOAT4(c[idx]) = tmp_c;
    }
}

template <typename T> 
inline T CeilDiv(const T& a, const T& b) {
    return (a + b - 1) / b;
}

int main(){

    size_t block_size = 128;
    size_t N =  32 * 1024 * 1024;
    size_t bytes_A = sizeof(float) * N;
    size_t bytes_B = sizeof(float) * N;
    size_t bytes_C = sizeof(float) * N;

    float* h_A = (float*)malloc(bytes_A);
    float* h_B = (float*)malloc(bytes_B);
    float* h_C = (float*)malloc(bytes_C);

    for( int i = 0; i < N; i++ ){
        h_A[i] = i / 666;
    }

    for( int i = 0; i < N; i++ ) {
        h_B[i] = i % 666;
    }

    float* d_A;
    float* d_B;
    float* d_C;

    checkCudaErrors(cudaMalloc(&d_A, bytes_A));
    checkCudaErrors(cudaMalloc(&d_B, bytes_B));
    checkCudaErrors(cudaMalloc(&d_C, bytes_C));

    checkCudaErrors(cudaMemcpy( d_A, h_A, bytes_A, cudaMemcpyHostToDevice));
    checkCudaErrors(cudaMemcpy( d_B, h_B, bytes_B, cudaMemcpyHostToDevice));

    cudaEvent_t start, stop;
    checkCudaErrors(cudaEventCreate(&start));
    checkCudaErrors(cudaEventCreate(&stop));
    float msec = 0;

    int iteration = 1;
    checkCudaErrors(cudaEventRecord(start));
    for(int i = 0; i < iteration; i++)
    {
        elementwise_add<<<CeilDiv(N, block_size), block_size>>>(d_A, d_B, d_C, N);                   
        //elementwise_add_float4<<<CeilDiv(N, block_size), block_size/4>>>(d_A, d_B, d_C, N);          
        //elementwise_add_float4<<<CeilDiv(N/4, block_size), block_size>>>(d_A, d_B, d_C, N);
    }

    checkCudaErrors(cudaEventRecord(stop));
    checkCudaErrors(cudaEventSynchronize(stop));
    checkCudaErrors(cudaEventElapsedTime(&msec, start, stop));
    printf("elementwise add takes %.5f msec\n", msec/iteration);

    checkCudaErrors(cudaMemcpy(h_C, d_C, bytes_C, cudaMemcpyDeviceToHost));
    for(int i = 0; i < N; i++){
        double err = fabs(h_C[i] - (h_A[i] + h_B[i]));
        if(err > 1.e-6) {
            printf("wrong answer!\n");
            break;
        }
    }

    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C);

    free(h_A);
    free(h_B);
    free(h_C);

    return 0;
}

编译和运行:

nvcc -o elementwise_add elementwise_add.cu 
./elementwise_add

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

27.HarmonyOS App(JAVA)可复用列表项的ListContainer

可复用列表项的ListContainer 简短的列表可以通过定向布局实现,但是如果列表项非常多,则使用定向布局就不再合适。如需要创建50个列表项的列表,那么用定向布局实现至少需要创建50个以上的组件了。然而,限于设备屏幕大小的限制,绝大多数组件不会显示在屏幕上,却会占据大量的内存…

CentOS 7.9.2009离线安装mysql 8.0客户端 (rpm包)

环境&#xff1a; #需求&#xff1a; 该服务器需要将csv文件入库到远端的mysql 服务器上。 CentOS Linux release 7.9.2009 (Core) 离线环境 &#xff0c;需安装mysql客户端 8.0.27#下载地址 https://downloads.mysql.com/archives/community/#按此顺序安装 rpm -ivh mysql…

C++ //练习 9.16 重写上一题的程序,比较一个list<int>中的元素和一个vector<int>中的元素。

C Primer&#xff08;第5版&#xff09; 练习 9.16 练习 9.16 重写上一题的程序&#xff0c;比较一个list中的元素和一个vector中的元素。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /**********************************…

AMEYA360 | 罗姆的EcoGaN™被台达电子Innergie品牌的45W输出AC适配器“C4 Duo”采用!

全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)的650V GaN器件(EcoGaN™)&#xff0c;被台达电子(Delta Electronics, Inc.&#xff0c;以下简称“台达”)Innergie 品牌的45W输出AC适配器(快速充电器)“C4 Duo” 采用。台达是基于IoT技术的绿色解决方案全球供应商。Inn…

AGI|教你用一部电影的时间训练个人专属Agent

目录 一、Agent如何工作&#xff1f; 二、Function Call 原理 三、开源模型工具调用微调 //Chat模型微调 //训练过程日志 //测试结果 //测试Tools 四、预训练模型微调 五、总结 Agent是一个超越简单文本生成的人工智能系统。它使用大型语言模型&#xff08;LLM&#x…

CS_上线三层跨网段机器(完整过程还原)

以前讲过用cs_smb_beacon上线不出网机器&#xff0c;但是真实的网络拓扑肯定不止这么一层的网络&#xff01; 所以我就来搭建一个复杂一点的网络环境&#xff01;&#xff01; 当然了&#xff0c;这三台电脑之间都是不同的网段&#xff0c;&#xff08;但是同属于一个域环境&a…

MySQL数据库进阶第五篇(锁)

文章目录 一、锁的概述二、全局锁三、表级锁四、元数据锁&#xff08;meta data lock, MDL&#xff09;五、意向锁六、行级锁七、行锁&#xff08;Record Lock&#xff09;八、间隙锁&#xff08;Gap Lock&#xff09;九、临键锁&#xff08;Next-Key Lock&#xff09;十、锁总…

遇见数字孪生城市美好模样 国产GIS加速创新竞逐“新赛道”

在未来数字孪生城市中&#xff0c;每一座建筑、每一条道路、每一盏路灯、每一辆车&#xff0c;甚至每一个人&#xff0c;都有其对应的数字孪生体。这些数字孪生体在虚拟世界中形成了城市的细致模型&#xff0c;如同镜子一样&#xff0c;反映出城市的实时状态&#xff0c;同时也…

Datawhale-Sora技术原理分享

目录 Sora能力边界探索 Sora模型训练流程 Sora关键技术拆解 物理引擎的数据进行训练 个人思考与总结 参考 https://datawhaler.feishu.cn/file/KntHbV3QGoEPruxEql2c9lrsnOb

HTB pwn Dragon Army

逆向分析 程序使用了alloca函数扩大了栈区 此处可以泄露libc的地址 程序主要功能在下面 while ( 1 ){while ( 1 ){fflush(stdin);fflush(_bss_start);fprintf(_bss_start, "\n%sDragons: [%d/%d]%s\n\n", "\x1B[1;34m", v5, 13LL, "\x1B[1;37m"…

山海鲸可视化:数据分析师的明智之选

作为一名资深的数据分析师&#xff0c;我深知数据可视化在数据分析和决策制定中的重要性。在众多的数据可视化工具中&#xff0c;我最终选择了山海鲸可视化软件。本文就简单介绍几点我选择它的主要原因&#xff1a; 首先&#xff0c;山海鲸可视化软件提供了强大的数据整合能力…

MySQL-事务,properties文件解析,连接池

1.事务机制管理 1.1 Transaction事务机制管理 默认情况下是执行一条sql语句就保存一次&#xff0c;那么比如我们需要三条数据同时成功或同时失败就需要开启事务机制了。开启事务机制后执行过程中发生问题就会回滚到操作之前&#xff0c;相当于没有执行操作。 1.2 事务的特征 事…

sql-labs25-28a

一、环境 网上都有不过多阐述 二、sql-labs第25关 它说你的OR和and属于它,那就是过滤了OR和and 注入尝试 不用or和and进行爆破注入,很明显是有注入点的 ?id-1 union select 1,2,3-- 查看数据库 ok&#xff0c;此道题算是解了但是如果我们用了and了呢 ?id-1 and updatex…

消息中间件篇之Kafka-高性能设计

一、高性能设计 消息分区&#xff1a;不受单台服务器的限制&#xff0c;可以不受限的处理更多的数据。 顺序读写&#xff1a;磁盘顺序读写&#xff0c;提升读写效率。 页缓存&#xff1a;把磁盘中的数据缓存到内存中&#xff0c;把对磁盘的访问变为对内存的访问。 零拷贝&a…

C# 水排序 微信小游戏

来只 水排序谜题启发式搜索方法_水排序解法小程序-CSDN博客 大神的C语言转换成C# 语言&#xff0c;更多的请看原作者&#xff0c;这里直接贴C#代码 using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace ConsoleApp2 {class Pro…

SImpAl

output matrix M&#xff0c;Curriculum using w ( x t , f , h ) w(x_t, f, h) w(xt​,f,h) 辅助信息 作者未提供代码

想露一手Linux命令,掌握这20个够用了!

中午好&#xff0c;我的网工朋友。 想做好网工&#xff0c;除了路由交换&#xff0c;掌握Linux操作系统也是很重要的。 因为很多服务器上都是用的Linux系统&#xff0c;要和服务器机器交互&#xff0c;就要通过Linux的相关命令。 正在找工作的朋友&#xff0c;如果面试时&am…

thinkphp6定时任务

这里主要是教没有用过定时任务没有头绪的朋友, 定时任务可以处理一些定时备份数据库等一系列操作, 具体根据自己的业务逻辑进行更改 直接上代码 首先, 是先在 tp 中的 command 方法中声明, 如果没有就自己新建一个, 代码如下 然后就是写你的业务逻辑 执行定时任务 方法写好了…

leetcode 2867. 统计树中的合法路径数目【筛质数+贡献法】

原题链接&#xff1a;2867. 统计树中的合法路径数目 题目描述&#xff1a; 给你一棵 n 个节点的无向树&#xff0c;节点编号为 1 到 n 。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi] 表示节点 ui 和 vi 在树中有一条边。 …

openai sora 只能根据文本生成视频?不,TA 是通用物理世界模拟器

视频生成模型作为世界模拟器 我们探索了在视频数据上进行大规模生成模型的训练。 具体来说&#xff0c;我们联合在可变持续时间、分辨率和长宽比的视频和图像上训练文本条件扩散模型。 我们利用了一个在视频和图像潜在编码的时空补丁上操作的变压器架构。 我们最大的模型So…