写一个 RTX 5080 上的 cuda gemm fp16

1. cpu 计算 fp16 四则运算

        由于会用到cpu 的gemm 与 gpu gemm 的对比验证,所以,这里稍微解释一下 cpu 计算fp16 gemm 的过程。这里为了简化理解,cpu 中不使用 avx 相关的 fp16 运算器,而是直接使用 cpu 原先的 ALU 功能。这里使用一个示例来做这件事情。

1.1. 源码编译运行

hello_fp16.cu


#include <stdio.h>
#include "cuda_fp16.h"int main()
{half x = half(3.333);half y = half(7.777);half z = half(0.0);z = x*y;printf("sizeof(half) = %ld x = %f \n", sizeof(x), float(z));return 0;
}

编译运行:

nvcc -g --gpu-architecture=sm_120 hello_fp16.cu -o hello_fp16

1.2. 调试追踪 fp16 的相关功能

        这里有两个目标:

        一个是类型转换,怎么样得到一个 fp16 的变量值;

        一个是 fp16 类型变量之间的乘法(四则运算)。

        现在看一下其中的 half(3.333) 的执行,通过使用 step,经历如下几个断点:

在 408 行 时,使用gdb )s 会跳到下图代码 549 行处:

继续使用 (cuda-gdb) s 会跳到下图:

    然后 使用 (cuda-gdb) next ,会经历上图代码的主体逻辑,也就是一些位运算的那些逻辑。

    结论便是,cuda 程序对 cpu 的 half(3.333) 使用了 cpu 软件算法模拟了这个转换过程,将 double 类型转换成 fp16,即 half 类型 

    同时接下来会发现,两个 half 类型的变量做乘法运算,会先将两个 half 转成 float,也是通过类似的软件模拟的转换方式,然后使用 cpu 的 float 乘法指令计算乘积,最后将 float 类型的乘积再转回 half 类型,存入 half 类型的变量内存中。

接下来调试 half 的乘法运算符 * :

在 执行到 z = x*y; 时,使用(cuda-gdb) step,会跳进half 类型的乘法运算符 * 的实现代码中,这里使用了 cpp 的重载功能(Operator Overloading) ,对运算符 * 做了重新实现 :

可以看到,operator * 重载时,函数体中调用了 __hmul(...) 来实现具体功能。

接下来继续使用 (cuda-gdb) step,看看 __hmul(...) 的实现:

        这里的 NV_IF_ELSE_TARGET(cond, , ) 表示可能存在两种可能的实现方式,根绝第一个表达式的真假来选择后边的第二个或者第三个表达式。因为我们使用了 sm_120, 不等于 sm_53,可以初步猜测是调用了后边的第三个表达式的内容来实现乘法。接下来通过 cuda-gdb 来单步调试验证一下。

        我们已经猜测会执行后边三行代码:2653,2654,2655等,但是为了验证,这里做了个新函数 hhhaddd(),插入到第三个表达式的中间 float xfa = hhhaddd(fa); :

        这会导致计算结果必然是错误的,但是可以给这个 hhhaddd 打断点,然后直接 continue,果然停在了这个函数上。

        说明执行了这三行代码,即,half 的乘法,是使用 float32 的乘法指令来实现的:

    const float fa = __half2float(a);const float fb = __half2float(b);return __float2half(fa * fb);

2. 写个 cpu gemm_fp16

        矩阵小一点,方便验证,其中的输出格式,是为了能够简单地放进matlab 做对比验证:


#include <stdio.h>
#include <stdlib.h>
#include "cuda_fp16.h"void init_matrix(half *A, int lda, int m, int n, bool colMajor)
{if(colMajor){for(int j=0; j<n; j++){for(int i=0; i<m; i++){half x = half(rand()*1.0f/RAND_MAX);A[i + j*lda] = x;printf(" %f",  float(x));}}printf("\n\n");}else{for(int i=0; i<m; i++){for(int j=0; j<n; j++){half x = half(rand()*1.0f/RAND_MAX);A[i*lda + j] = x;printf(" %f",  float(x));}}}
}void print_matrix(half *A, int lda, int m, int n, bool colMajor)
{printf("[ ...\n");for(int i=0; i<m; i++){for(int j=0; j<n; j++){if(colMajor)printf(" %5.4f, ", float(A[i + j*lda]));elseprintf(" %5.4f, ", float(A[i*lda + j]));}printf(" ; ...\n");}printf("]\n");
}void gemm_fp16_cpu(int M, int N, int K,half* A, int lda,half* B, int ldb,half* C, int ldc,half alpha, half beta)
{for(int i=0; i<M; i++){for(int j=0; j<N; j++){half sigma = half(0.0);for(int k=0; k<K; k++){sigma += A[i + k*lda]*B[k + j*lda];}C[i + j*ldc] = alpha*sigma + beta*C[i + j*ldc];}}
}int main()
{int m = 4;int n = 4;int k = 4;int lda = m;int ldb = k;int ldc = m;half *A_h;half *B_h;half *C_h;half alpha = half(1.0);half beta  = half(1.0);A_h = (half*)malloc(lda * k * sizeof(half));B_h = (half*)malloc(ldb * n * sizeof(half));C_h = (half*)malloc(ldc * n * sizeof(half));init_matrix(A_h, lda, m, k, true);init_matrix(B_h, ldb, k, n, true);init_matrix(C_h, ldc, m, n, true);printf("A_h =");print_matrix(A_h, lda, m, k, true);printf("B_h =");print_matrix(B_h, ldb, k, n, true);printf("C_h =");print_matrix(C_h, ldc, m, n, true);gemm_fp16_cpu(m, n, k, A_h, lda, B_h, ldb, C_h, ldc, alpha, beta);printf("C_h =");print_matrix(C_h, ldc, m, n, true);return 0;
}

Makefile

EXE := hello_gemm.fp16all: $(EXE)%: %.cunvcc --gpu-architecture=sm_120 -g $< -o $@ -I /usr/local/cuda/include.PHONY: clean
clean:-rm -rf $(EXE)

编译运行

$ make

octave 验证

误差范围内,结果是相等的。

3. GPU 的最简单版本 gemm_v01

        简单主要是指没有任何优化考虑。单个warp 工作,也不考虑数据复用、异步加载,不考虑 tensor core 加速,流水线等都不考虑。

我们可以先稍微看看 RTX 5080 的硬件信息:

10752 个cuda core,每个warp 占 32 个 cuda core【注,从 Ampere 开始,每个warp 同时占用 32 个 cuda core;之前架构是 16 个 cuda core 迭代两次完成 32 个 thread  的任务;】,
总共含 84 个sm,
所以,每个sm 存在 128个 cuda core,也就是 128/32 = 4 个 同时运行的 warp,也即 4 个 tensor core/sm;也就是每个 block 最多可以同时占用 4 个tensor core。

这个 v01 版本不考虑使用 tensor core,仅启动单个warp 工作。

ex/hello_gemm.fp16.cu


#include <stdio.h>
#include <stdlib.h>
#include "cuda_fp16.h"void init_matrix(half *A, int lda, int m, int n, bool colMajor)
{if(colMajor){for(int j=0; j<n; j++){for(int i=0; i<m; i++){half x = half(rand()*1.0f/RAND_MAX);A[i + j*lda] = x;printf(" %f",  float(x));}}printf("\n\n");}else{for(int i=0; i<m; i++){for(int j=0; j<n; j++){half x = half(rand()*1.0f/RAND_MAX);A[i*lda + j] = x;printf(" %f",  float(x));}}}
}void print_matrix(half *A, int lda, int m, int n, bool colMajor)
{printf("[ ...\n");for(int i=0; i<m; i++){for(int j=0; j<n; j++){if(colMajor)printf(" %5.4f, ", float(A[i + j*lda]));elseprintf(" %5.4f, ", float(A[i*lda + j]));}printf(" ; ...\n");}printf("]\n");
}void gemm_fp16_cpu(int M, int N, int K,half* A, int lda,half* B, int ldb,half* C, int ldc,half alpha, half beta)
{for(int i=0; i<M; i++){for(int j=0; j<N; j++){half sigma = half(0.0);for(int k=0; k<K; k++){sigma += A[i + k*lda]*B[k + j*lda];}C[i + j*ldc] = alpha*sigma + beta*C[i + j*ldc];}}
}__global__ void gemm_v01_fp16_all(int M, int N, int K,half* A, int lda,half* B, int ldb,half* C, int ldc,half alpha, half beta)
{unsigned int i = threadIdx.x;unsigned int j = threadIdx.y;if(i==16) printf("%d ", j);
printf("threadIdx.x=%d  ", threadIdx.x);half sigma = half(0.0);for(unsigned int k = 0; k<K; k++){sigma += A[i + k*lda]*B[k + j*ldb];}C[i + j*ldc] = alpha*sigma + beta*C[i + j*ldc];
}void gemm_v01_test(int m, int n, int k,half* Ah, int lda,half* Bh, int ldb,half* Ch, int ldc,half alpha, half beta,half* Cd2h)
{//1. alloc ABC_dhalf * Ad = nullptr;half * Bd = nullptr;half * Cd = nullptr;cudaMalloc((void**)Ad, lda*k*sizeof(half));cudaMalloc((void**)Bd, ldb*n*sizeof(half));cudaMalloc((void**)Cd, ldc*n*sizeof(half));//2. cpy H2DcudaMemcpy(Ad, Ah, lda*k*sizeof(half), cudaMemcpyHostToDevice);cudaMemcpy(Bd, Bh, ldb*n*sizeof(half), cudaMemcpyHostToDevice);cudaMemcpy(Cd, Ch, ldc*n*sizeof(half), cudaMemcpyHostToDevice);//3. Gemm_v01, simple cuda core gemmdim3 block_;dim3 grid_;block_.x = 32;block_.y = 32;grid_.x = 1;grid_.y = 1;printf("__00________\n");gemm_v01_fp16_all<<<grid_,block_>>>(m, n, k, Ad, lda, Bd, ldb, Cd, ldc, alpha, beta);
printf("##11########\n");//4. cpy D2HcudaMemcpy(Cd2h, Cd, ldc*n*sizeof(half), cudaMemcpyDeviceToHost);//5. free ABC_dcudaFree(Ad);cudaFree(Bd);cudaFree(Cd);
}
int main()
{int m = 32;int n = 32;int k = 32;int lda = m;int ldb = k;int ldc = m;half *A_h;half *B_h;half *C_h;half *C_d2h;half alpha = half(1.0);half beta  = half(1.0);A_h = (half*)malloc(lda * k * sizeof(half));B_h = (half*)malloc(ldb * n * sizeof(half));C_h = (half*)malloc(ldc * n * sizeof(half));C_d2h = (half*)malloc(ldc * n * sizeof(half));init_matrix(A_h, lda, m, k, true);init_matrix(B_h, ldb, k, n, true);init_matrix(C_h, ldc, m, n, true);printf("A_h =");print_matrix(A_h, lda, m, k, true);printf("B_h =");print_matrix(B_h, ldb, k, n, true);printf("C_h =");print_matrix(C_h, ldc, m, n, true);gemm_fp16_cpu(m, n, k, A_h, lda, B_h, ldb, C_h, ldc, alpha, beta);printf("C_h =");print_matrix(C_h, ldc, m, n, true);gemm_v01_test(m, n, k, A_h, lda, B_h, ldb, C_h, ldc, alpha, beta, C_d2h);printf("C_d2h =");print_matrix(C_d2h, ldc, m, n, true);return 0;
}

未完待续

。。。。

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

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

相关文章

web渗透PHP反序列化漏洞

web渗透PHP反序列化漏洞1&#xff09;PHP反序列化漏洞反序列我们可以控制对象中的值进行攻击O:1:"C":1:{s:3:"cmd";s:8:"ipconfig";}http://127.0.0.1/1.php?xO:1:%22C%22:1:{s:3:%22cmd%22;s:3:%22ver%22;}常见的反序列化魔术方法&#xff1a;…

FPGA学习笔记——SPI读写FLASH

目录 一、任务 二、需求分析 三、Visio图 四、具体分析 五、IP核配置 六、代码 七、实验现象 一、任务 实验任务&#xff1a; 1.按下按键key1&#xff0c;开启读ID操作&#xff0c;将读出来的ID&#xff0c;通过串口发送至PC端显示&#xff0c;显示格式为“读ID:XX-XX-XX…

一句话PHP木马——Web渗透测试中的隐形杀手

文章目录前言什么是"一句话木马"&#xff1f;常见变种与隐藏技巧1. 函数变种2. 加密混淆3. 变量拆分4. 特殊字符编码上传技巧与绕过防御常见上传绕过技巧检测与防御措施1. 服务器配置2. 上传验证3. 代码审计4. Web应用防火墙(WAF)实战案例分析深度思考&#xff1a;安…

房屋租赁系统|基于SpringBoot和Vue的房屋租赁系统(源码+数据库+文档)

项目介绍 : SpringbootMavenMybatis PlusVue Element UIMysql 开发的前后端分离的房屋租赁系统&#xff0c;项目分为管理端和用户端以及房主端 项目演示: 基于SpringBoot和Vue的房屋租赁系统 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上…

C++动态规划——经典题目(下)

上一篇文章没有写全&#xff0c;这篇再补两道题酒鬼#include<bits/stdc.h> using namespace std; int dp[110][10]{0}; int a[1010]{0}; int n,m; int main() {cin>>n;dp[0][0]0;dp[1][0]0;dp[1][1]a[1];for(int i1;i<n;i){cin>>a[i];}for(int i2;i<n;…

介绍Ansible和实施Ansible PlayBook

第一章 介绍Ansible1. ansible的特点是什么&#xff1f;a. ansible使用yaml语法&#xff0c;语言格式简洁明了。b. ansible不需要代理&#xff0c;仅仅通过SSH远程连接就可以控制受管主机&#xff0c;是一种非常便捷、安全的方法。c. Ansible的功能强大&#xff0c;可以利用ans…

ComfyUI驱动的流程化大体量程序开发:构建上下文隔离的稳定系统

ComfyUI驱动的流程化大体量程序开发&#xff1a;构建上下文隔离的稳定系统 在现代软件工程中&#xff0c;随着程序体量的不断增长&#xff0c;上下文污染&#xff08;Context Pollution&#xff09;和状态依赖混乱已成为导致系统不稳定、调试困难、维护成本高昂的核心问题。尤…

基于SpringBoot的协同过滤余弦函数的美食推荐系统(爬虫Python)的设计与实现

基于SpringBootvue的协同过滤余弦函数的个性化美食(商城)推荐系统(爬虫Python)的设计与实现 1、项目的设计初衷&#xff1a; 随着互联网技术的快速发展和人们生活水平的不断提高&#xff0c;传统的美食消费模式已经无法满足现代消费者日益个性化和多样化的需求。在信息爆炸的时…

机器视觉学习-day19-图像亮度变换

1 亮度和对比度亮度&#xff1a;图像像素的整体强度&#xff0c;亮度提高就是所有的像素加一个固定值。对比度&#xff1a;当对比度提高时&#xff0c;图像的暗部与亮部的差值会变大。OpenCV调整图像亮度和对比度的公式使用一个&#xff1a;代码实践步骤&#xff1a;图片输入→…

redis详解 (最开始写博客是写redis 纪念日在写一篇redis)

Redis技术 1. Redis简介 定义与核心特性&#xff08;内存数据库、键值存储&#xff09; Redis&#xff08;Remote Dictionary Server&#xff0c;远程字典服务&#xff09;是一个开源的、基于内存的高性能键值存储数据库&#xff0c;由 Salvatore Sanfilippo 编写&#xff0c;用…

【MD文本编辑器Typora】实用工具推荐之——轻量级 Markdown 编辑器Typora下载安装使用教程 办公学习神器

本文将向大家介绍一款轻量级 Markdown 编辑器——Typora&#xff0c;并详细说明其下载、安装与基本使用方法。 引言&#xff1a; MD 格式文档指的是使用 Markdown 语言编写的文本文件&#xff0c;其文件扩展名为 .md。 Markdown 是一种由约翰格鲁伯&#xff08;John Gruber&am…

Vue2+Element 初学

大致实现以上效果 一、左侧自动加载菜单NavMenu.vue 菜单组件&#xff0c;简单调整了一下菜单直接的距离&#xff0c;代码如下&#xff1a;<template><div><template v-for"item in menus"><!-- 3、有子菜单&#xff0c;设置不同的 key 和 inde…

Shell编程知识整理

文章目录一、Shell介绍1.1 简介1.2 Shell解释器二、快速入门2.1 编写Shell脚本2.2 执行Shell脚本2.3 小结三、Shell程序&#xff1a;变量3.1 语法格式3.2 变量使用3.3 变量类型四、字符串4.1 单引号4.2 双引号4.3 获取字符串长度4.4 提取子字符串4.5 查找子字符串五、Shell程序…

AI与低代码的激情碰撞:微软Power Platform融合GPT-4实战之旅

引言 在当今数字化飞速发展的时代,AI 与低代码技术正成为推动企业变革的核心力量。AI 凭借其强大的数据分析、预测和决策能力,为企业提供了智能化的解决方案;而低代码开发平台则以其可视化、快速迭代的特性,大大降低了应用开发的门槛和成本。这两者的结合,开启了一场全新的…

豆包1.6+PromptPilot实战:构建智能品牌评价情感分类系统的技术探索

豆包1.6PromptPilot实战&#xff1a;构建智能品牌评价情感分类系统的技术探索 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;…

如何在VsCode中使用git(免敲命令版本!保姆级!建议收藏!)

目录 文章目录 前言 一、电脑安装git 二、在vscode安装git插件 三、克隆仓库 四、提交代码 五、创建分支、切换分支、合并分支 1、创建分支 2、切换分支 3、合并分支 六、创建标签和推送标签 七、解决冲突 八、拉取、抓取仓库 九、Reivew代码 总结 前言 随着Vscode的推出和普及…

3.kafka常用命令

在 0.9.0.0 之后的 Kafka&#xff0c;出现了几个新变动&#xff0c;一个是在 Server 端增加了 GroupCoordinator 这个角色&#xff0c;另一个较大的变动是将 topic 的 offset 信息由之前存储在 zookeeper 上改为存储到一个特殊的 topic&#xff08;__consumer_offsets&#xff…

主从DNS和Web服务器搭建过程

完整服务器搭建流程 环境说明 主服务器&#xff1a;192.168.102.128 - DNS Web 从服务器&#xff1a;192.168.102.133 - 从DNS 网站&#xff1a;www.zhangsan.com (HTTPS加密)、www.lisi.com (HTTP) 手动配置主服务器和从服务器的ip地址&#xff0c;dns&#xff0c;网关…

信号无忧,转决千里:耐达讯自动化PROFIBUS集线器与编码器连接术

在工业自动化领域&#xff0c;尤其是高端装备制造、智能产线、精密运动控制等场景中&#xff0c;系统稳定性与信号实时性一直是工程师关注的核心。随着设备智能化程度不断提高&#xff0c;编码器作为运动控制的关键反馈元件&#xff0c;其数量与分布密度显著增加&#xff0c;对…

大模型微调示例四之Llama-Factory-DPO

大模型微调示例四之Llama-Factory-DPO一、强化学习数据处理二、配置训练文档三、模型预测一、强化学习数据处理 原始数据地址&#xff1a;https://nijianmo.github.io/amazon/index.html 第一步&#xff1a;读取 video game 信息 import codecs, json, re from random impor…