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

news2025/1/11 7:53:02

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

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

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

相关文章

table展示子集踩坑

##elemenui中table通过row中是否有children进行判断是否展示子集&#xff0c;通过设置tree-prop的属性进行设置&#xff0c;子级的children的名字可以根据自己的子级名字进行替换&#xff0c;当然同样可以对数据处理成含有chilren的子级list。 问题&#xff1a; 1.如果是根据后…

wpf 简单实验 数据更新 列表更新

1.概要 1.1 需求 一个列表提供添加修改删除的功能&#xff0c;添加和修改的内容都来自一个输入框 1.2 要点 DisplayMemberPath"Zhi"列表.ItemsSource datalist;(列表.SelectedItem ! null)(列表.SelectedItem as A).Zhi 内容.Text;datalist.Remove((列表.Selec…

YUV Format

title: 音视频同步 date: 2024-02-26 21:01:24 comments: true #是否可评论 toc: true #是否显示文章目录 categories: #分类 - 多媒体技术 tags: #标签 - 多媒体技术 summary: YUV Format YUV Format 简介 国际视频组织定义了多种yuv数据格式。文章中每一个像素都只有8bits…

大佬推荐的网络安全学习路线(从基础到高级)

说起网络安全&#xff0c;你可能会担心它是一个过时的行业。有人说&#xff0c;网络安全快卷死了&#xff0c;你既要攻又要防&#xff0c;并且随着技术的发展&#xff0c;你还要不断地学习&#xff0c;不在卷中生&#xff0c;就在卷中死。 实际上&#xff0c;随着数字化进程的…

Nest.js权限管理系统开发(八)jwt登录

安装相关依赖 虽然仅使用nestjs/jwt就能实现身份验证的功能&#xff0c;但是使用passport能在更高层次上提供更多便利。Passport 拥有丰富的 strategies 生态系统&#xff0c;实现了各种身份验证机制。虽然概念简单&#xff0c;但你可以选择的 Passport 策略集非常丰富且种类繁…

【性能测试】企业性能测试-并发用户数估算(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 并发用户数&#…

05 Flink 的 WordCount

前言 本文对应于 spark 系列的 Spark 的 WordCount 这里主要是 从宏观上面来看一下 flink 这边的几个角色, 以及其调度的整个流程 一个宏观 大局上的任务的处理, 执行 基于 一个本地的 flink 集群 测试用例 /*** com.hx.test.Test01WordCount** author Jerry.X.He* ver…

第十二章 Linux——日志管理

第十二章 Linux——日志管理 基本介绍系统常用日志日志管理服务日志轮替基本介绍日志轮替文件命名logrotate配置文件自定义加入日志轮转应用实例 日志轮替机制原理查看内存日志 基本介绍 日志文件是重要的系统信息文件&#xff0c;其中记录了许多重要的系统事件&#xff0c;包…

跨浏览器测试:如何确保你的应用在各种浏览器上都能正常运行

在当今的互联网时代&#xff0c;浏览器已成为我们获取信息、与他人交流、工作和娱乐的主要工具。然而&#xff0c;不同的浏览器、不同的版本和不同的操作系统可能会对你的应用造成不同的影响&#xff0c;可能使其表现出各种不同的行为和问题。为了确保你的应用能在各种浏览器环…

F2图例封装 - Bar

基于vue3 和 F2 3.5.0 <template><div :style"{minHeight: ${height}px,width: 100% }" ref"container"><canvas v-show"showChart" :id"chartId" class"chart-canval"></canvas><empty-box v-…

人工智能产生的幻觉问题真的能被看作是创造力的另一种表现形式吗?

OpenAI的首席执行官山姆奥特曼&#xff08;Sam Altman&#xff09;曾声称&#xff0c;人工智能产生的“幻觉”其实未尝不是一件好事&#xff0c;因为实际上GPT的优势正在于其非凡的创造力。 目录 一.幻觉问题的概念 二.幻觉产生的原因 三.幻觉的分类 四.减轻AI的幻觉问题到…

【云原生】Spring Cloud Gateway的底层原理与实践方法探究

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &#x1f680; 本…

【深度学习笔记】深度学习训练技巧

深度学习训练技巧 1 优化器 随机梯度下降及动量 随机梯度下降算法对每批数据 ( X ( i ) , t ( i ) ) (X^{(i)},t^{(i)}) (X(i),t(i)) 进行优化 g ∇ θ J ( θ ; x ( i ) , t ( i ) ) θ θ − η g g\nabla_\theta J(\theta;x^{(i)},t^{(i)})\\ \theta \theta -\eta g g…

Aigtek前置微小信号放大器在传感器检测中的应用有哪些

传感器是将物理量转换为电信号的装置&#xff0c;其精度和灵敏度直接影响到检测系统的性能。而传感器的输出信号通常都非常微弱&#xff0c;需要进行放大处理才能得到可靠的测量结果。前置微小信号放大器&#xff0c;作为一种重要的传感器检测元件&#xff0c;在传感器检测中发…

苏宁商品详情大揭秘:一键解锁API接口,电商数据尽在掌握

苏宁商品详情API接口技术深度探索 一、引言 在电商领域&#xff0c;获取商品详情是许多业务场景的基础需求。苏宁商品详情API接口为此提供了便捷的途径。本文将带你深入了解苏宁商品详情API接口的技术细节&#xff0c;帮助你更好地利用这一接口&#xff0c;提升业务效率。 二…

春节医美热,爱美客、昊海生科谁更赚钱?

在颜值经济赛道上&#xff0c;医美项目逐渐成为消费主流。随着春节假期的到来&#xff0c;医美消费又将迎来高峰期。 “医美三剑客”中&#xff0c;爱美客(300896.SZ)、昊海生科(688366.SH)近日相继公布了2023年的业绩报告&#xff1a;2023年&#xff0c;爱美客预计实现净利润…

[机器视觉]halcon应用实例 边缘检测

一个学习找边的实例 边缘检测的步骤图解 步骤 1.通过Blob方法获取需要测量的Region 1.1 主要运用图像形态学、二值化 2.创建测量句柄 2.1 create_metrology_model (MetrologyHandle) 3.设置目标图像大小 3.1 set_metrology_model_image_size (MetrologyHandle, Width, Height)…

学会玩游戏,智能究竟从何而来?

最近在读梅拉妮米歇尔《AI 3.0》第三部分第九章&#xff0c;谈到学会玩游戏&#xff0c;智能究竟从何而来&#xff1f; 作者: [美] 梅拉妮米歇尔 出版社: 四川科学技术出版社湛庐 原作名: Artificial Intelligence: A Guide for Thinking Humans 译者: 王飞跃 / 李玉珂 / 王晓…

Helm vs Kustomize 深度比较

Helm和Kustomize都是流行的Kubernetes集群部署管理工具&#xff0c;本文比较了两者的优缺点&#xff0c;方便读者根据项目实际情况采用适合的方案。原文: Helm vs Kustomize: why, when, and how[1] 挑战 开始讨论之前&#xff0c;先来看看为什么要使用 Helm 或 Kustomize。 这…

Mac使用K6工具压测WebSocket

commend空格 打开终端&#xff0c;安装k6 brew install k6验证是否安装成功 k6 version设置日志级别为debug export K6_LOG_LEVELdebug执行脚本&#xff08;进入脚本所在文件夹下&#xff09; k6 run --vus 100 --duration 10m --out csvresult.csv script.js 脚本解释&…