【深度学习推荐系统 工程篇】三、浅析FastTransFormer看 GPU推理优化 思路

news2025/1/11 8:10:01

前言

在搜索/推荐场景中(一般是CTR/CVR预估)Serving的模型一般是稀疏参数占比比较大,工程落地方面会遇到两方面的困难:

  • 稀疏参数的存储/IO
  • 网络结构的优化

对于稀疏参数的存储/IO,在上一篇【深度学习推荐系统 工程篇】二、从TF-Serving看生产环境的模型推理服务 有提及,这篇是想总结下 网络结构的优化

本篇借助分析FasterTransformer框架,看Transformer在工程落地中需要做什么样的优化,然后总结一些通用的优化思路

读完这篇文章,希望读者可以:

  1. 了解FasterTransformer整体框架以及流程
  2. 了解推理过程中GPU的优化思路和方法

一、Transformer算法理论基础

关于Transformer的网络结构,网上都有了各种分析,但大多良莠不齐,随便找一个看大概率容易一头雾水;

1.1 Transformer相关资料

这里分享下找到比较优秀的资料

  • Transformer原论文:https://arxiv.org/abs/1706.03762
  • 李沐精读论文:https://www.bilibili.com/video/BV1pu411o7BE/
  • Transformer详解:http://jalammar.github.io/illustrated-transformer/
  • Harvard NLP 团队用Pytorch实现Transformer:http://nlp.seas.harvard.edu/annotated-transformer/
  • Tensor2Tensor代码:https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/models/transformer.py

看完这几个,基本可以对Transformer的结构有大致的了解。

1.2 理论计算复杂度

如果想要进行工程落地,最好对Transformer理论上的计算复杂度有个大概了解;

在实际推理过程中,假设输入的BatchSize为 M M M,每条的SequenceLength为 N N N

网络的hidden_dim为 D D D,Encoder个数为 e n c o d e r _ s i z e encoder\_size encoder_size,Decoder的个数为 d e c o d e r _ s i z e decoder\_size decoder_size

一次推理的计算复杂度:

O ( M N ˙ 2 D ˙ ) ∗ ( e n c o d e r _ s i z e + d e c o d e r _ s i z e ) O(M \dot N^2 \dot D) * (encoder\_size +decoder\_size) O(MN˙2D˙)encoder_size+decoder_size

PS:计算量的大头 是 Attention结构中QKV的计算

二、浅析FastTransformer框架

2.1 框架目录

本次分析FastTransformer源码是v5.0版本,项目地址:https://github.com/NVIDIA/FasterTransformer

项目框架比较清晰,将Attention结构封装成Layer,内部再实现调用高度优化的kernel函数

目录结构如下:

/src/fastertransformer: source code of FasterTransformer
|–/models: Implementation of different models, like BERT, GPT.(常用网络的实现)
|–/layers: Implementation of layer modeuls, like attention layer, ffn layer.(封装成Layer模块,如attention/ffn等结构)
|–/kernels: CUDA kernels for different models/layers and operations, like addBiasResiual.(CUDA Kernel算子实现,一般在layer中调用)
|–/tf_op: custom Tensorflow OP implementation(封装成TensorflowOp)
|–/th_op: custom PyTorch OP implementation(封装成PytorchOp)
|–/triton_backend: custom triton backend implementation()
|–/utils: Contains common cuda utils, like cublasMMWrapper, memory_utils(封装成的工具类,如cublas参数,Allocator接口,Tensor接口)

上述目录中,最重要的是layers和kernels,layer抽象了计算流程,kernel给出了高效实现;

在Transformer结构中,两个重要的layer就是AttentionLayer和FFNLayer,这里画了类图整理下层次关系:

Attention layer:
在这里插入图片描述
FFN layer:
在这里插入图片描述
整体层次比较简单,下面以Example里的Bert模型为例,看下Forward流程

2.2 Bert模型的Forward流程

相应的代码在:https://github.com/NVIDIA/FasterTransformer/blob/release/v5.0_tag/src/fastertransformer/models/bert/Bert.h
相应的文档:https://github.com/NVIDIA/FasterTransformer/blob/release/v5.0_tag/docs/bert_guide.md

Bert模型主要包括 AttentionLayer和FFNLayer,可通过参数配置具体实现:

// Attention Layer
enum class AttentionType
{
    UNFUSED_MHA,
    UNFUSED_PADDED_MHA,
    FUSED_MHA,
    FUSED_PADDED_MHA
};
// FFN Layer
enum ActivationType
{
    Gelu,
    Relu
};

Bert模型的一次Forward流程:

在这里插入图片描述

2.3 Forward流程中的优化思路

2.3.1 流程优化(Remove Padding)

在这个文档中 https://github.com/NVIDIA/FasterTransformer/blob/release/v5.0_tag/docs/bert_guide.md 给出了去除Padding的流程图,

一个Batch中,对于没有达到MaxSeqLen的输入,一般会进行padding,补齐到MaxSeqLen,但显而易见会引入不必要的计算;

Effective-transformer 引入segment offset数组来去除Padding;FasterTransformer把这个优化集成到了项目中

2.3.2 OpFusion

OpFusion是网络结构优化中常见的手段,主要是以下几点目标:

  • 减少 Kernel Launch 次数以及开销
  • 在CPU/GPU异构架构中,如果部分算子仅有CPU实现的话会造成频繁的H2D/D2H,用GPU实现相应的cpu算子逻辑,可以避免HostMem,DeviceMem互相拷贝
  • 即使相邻的两个Op均有GPU实现,融合成一个算子,也可以减少计算结果显存读写的开销(否则需要先写回GlobalMemory,再从GlobalMemory读

回到FastTransformer,它将琐碎的操作尽可能的合并成一个Kernel;

PS:Tensorflow框架的一个特点是算子比较琐碎,一个操作经常由多个基础算子组成,因此针对Tensorflow的模型,OpFusion几乎是上线推理前必做的优化手段

2.3.3 优化Gemm

这里主要有几点:

  • 对QKV计算,调用Gemm或者BatchedGemm
  • FP16/FP32分别调用不同的gemm api接口(fp32 use cublas as default,fp16 use cublasLt as default)
  • AutoTunning

这里想分享2点:

一个是NV的线性计算库

看代码的时候发现用到Blas库比较多,除了基础的cublas接口,还有cuBlasLT,cuBlasXT等等,特意查了下资料:

  1. cuBlas/cuBlasLT简单的介绍和区别:https://developer.nvidia.com/blog/new-cublas-12-0-features-and-matrix-multiplication-performance-on-nvidia-hopper-gpus/
  2. cuBLas的官方API:https://docs.nvidia.com/cuda/cublas/index.html

二是针对Gemm/Kernel函数的AutoTunning思路

背景:由于GPU架构的复杂性,没有一组通用的参数,可以让某一操作 在所有数据规模/硬件 上达到最优的性能

在这种限制下,一种可能的做法 是将可调的参数独立出来组成参数空间(如 O ( b a t c h _ s i z e , m , n , k , d a t a _ t y p e ) O(batch\_size, m, n, k, data\_type) O(batch_size,m,n,k,data_type) 这几个参数够成了参数空间),在上线前,针对线上的真实请求,遍历参数空间中的性能指标,挑出最优的一组 写到 config文件里 使用

适用的场景不仅包括Gemm,也可以对Kernel函数的BlockSize, ThreadSize进行调整

2.3.4 低精度优化

  • 使用half2类型,进行向量化读取操作,在相同的指令周期内处理更多数据(类似于CPU上的AVX指令集操作),提升吞吐
  • FP16发挥硬件TensorCore的能力,加速计算

关于half2,额外找了些资料:

half2提升数据读取:https://developer.nvidia.com/blog/cuda-pro-tip-increase-performance-with-vectorized-memory-access/
关于half2与half的区别:https://forums.developer.nvidia.com/t/half2-vs-half-datatype/219492/3

另外关于低精度还想再分享一点,就是能降存储

最近比较火的大模型动辄上百亿参数,很难进行进行单机加载(近期发布的BaiChuan-13B,模型大小约30GB),

如果想在显存较小的显卡上加载,只能尝试低精度的方式(FP16或量化)降低数据存储(不过会影响网络精度,需要客观评估带来的业务影响)

2.3.5 高效的Kernel函数实现

项目中Kernel函数上都实现的很高效,如Reduce操作,都可以当做CUDA编程例子来学,

主要的点:

  • shared_memory 加速读取
  • __ldg缓存的使用
  • #pragma unroll
  • half2/INT8 低精度实现

三、GPU 推理优化思路

3.1 GPU推理存在的问题

谈问题之前,我们首先设想一个能充分发挥GPU算力的完美推理框架,它应该是这样

  • 几乎没有Kernel lanuch开销(NVProf中Kernel之间没有空隙),最好所有的操作都能在一个Kernel中完成
  • 接近全部的时间都用在 计算 上,达到硬件算力指标

但实际情况中,:

  1. Kernel Lanuch开销总是存在
  2. 永远会有访存的开销(尤其GPU中的GlobalMemory / SharedMemory 访问差了一个数量级,使得访存可能成为计算的瓶颈)

这些问题是GPU硬件架构 客观决定的,所以我们的优化工作只能是尽力缓解,然后去逼近理论计算的上限

3.2 可能的优化思路

针对上面提到的问题,我们可以总结下可能优化思路(在2.3中已经提到):

  • 流程优化
  • OpFusion
  • Gemm优化
  • 低精度优化
  • 高效的Kernel实现

基本上涵盖了可能的思路

3.3 “No silver bullet”(没有银弹)

另外对于模型的优化,笔者想借助软件工程的一句经典名言来发表下自己的想法:“No sliver bullet”,

即优化框架的通用性 和 极致性能很难兼得,没有一种框架可以对 现在所有的网络结构以及未来可能出现的结构 提供极致的优化

优化 的 过程永远是 螺旋式的迭代前进,即

  1. 新的网络结构(算法)
  2. Perf分析热点,进行可能优化,抽出通用的部分作为框架(工程)
  3. 新的网络结构(算法)
  4. Perf分析热点,进行可能优化,抽出通用的部分作为框架(工程)

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

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

相关文章

MySQL查询(基础到高级)

目录 一、单表查询: 1.基本查询: 1.1 查询多个字段: 1.2 去除重复记录: 2. 条件查询: 2.1 语法 2.2 条件分类: 比较运算符: between..and..使用示例: ​编辑 in(..) 使用示例&…

InfiniTAM v3中localVBA的使用原理

注意:以下是我对InfiniTAM v3代码的理解 初始化:假设预先设定的block数量长度为10,localVBA初始化后如下图 voxelBlocks的内存已经分配,但都是空的allocationList被初始化为allocationList[i]i,表示任意访问allocatio…

【数据开发】数据全栈知识架构,数据(平台、开发、管理、分析)

文章目录 一、数据全栈知识架构1、数据方法(思维,统计学,实践,北极星)2、数据工具:数据仓库3、数据规范 二、数据分析工具1、大数据平台2、数据开发:入库计算(重点)3、数…

WP篇 某网杯WEBAWD踩坑

某网杯 事件回顾赛题环境 坑木马访问JSP显示空白Ubuntu与Mysql问题框架选择idea 事件回顾 虽然没有参与本次AWD,但是看到另外一只队伍down下来的文件,我还是忍不住心动了,于是决定复现一番,因为某些原因,详细的wp和复…

c#:System.Text.Json 的使用三(从Newtonsoft迁移)

环境: .net 6.0vs2022 系列篇: 《c#:System.Text.Json 的使用一》 《c#:System.Text.Json 的使用二》 《c#:System.Text.Json 的使用三(从Newtonsoft迁移)》 参考: 《MSDN: 从 Newt…

【vue2第二十章】vuex使用 (state,mutations,actions,getters)

vuex是什么? Vuex是一个用于Vue.js应用程序的状态管理模式。它允许您在应用程序中管理共享状态,并以可预测的方式进行状态更新。Vuex集成了Vue的响应式系统,使得状态的变化能够自动地更新视图。使用Vuex,您可以将应用程序的状态集…

华为云云耀云服务器L实例评测 |云服务器性能评测

通过上一篇文章华为云云耀云服务器 L 实例评测 |云服务器选购,我已经购买了一台 Centos 系统的云耀云服务器 L 实例。 在获得云耀云服务器 L 实例后,首要任务是熟悉云耀云服务器 L 实例的性能,对云耀云服务器 L 实例的性能进行测…

如何在 SOLIDWORKS中创建零件模板 硕迪科技

作为一款多功能且可大量定制的 3D CAD 软件,SOLIDWORKS模板可以通过自定义属性包含大量数据。可以通过为SOLIDWORKS零件、装配体和工程图创建模板来利用这些模板。 与其他一些CAD软件不同,SOLIDWORKS不限制您可以创建的模板数量 - 您可以根据需要创建任…

3、SpringBoot_配置文件

四、配置文件 1.前言 曾经使用SpringMVC的时候是手动修改tomcat配置的端口信息,那现在Springboot如何修改?springboot有一个默认的配置文件 application.properties 2.配置文件分类 常用配置信息官方文档地址 https://docs.spring.io/spring-boot/doc…

web:[极客大挑战 2019]Knife

题目 点开页面显示为,还有一句话木马 查看源代码,没有什么特别的 回归题目,页面显示使用菜刀和一句话木马,使用蚁剑连接 在根目录找到了flag文件

记账APP:小哈记账5——记账首页页面的制作(2)

项目介绍: 小哈记账是一款用于记账APP,基于Android Studio开发工具,采用Java语言进行开发,同时使用litepal和阿里云数据库进行数据的增删查改,以图标的形式在App的界面上显示。App可以清晰显示收支情况,并以…

EfficientNet笔记

前言 论文 EfficientNet 是一系列卷积神经网络架构,它旨在在计算资源受限的情况下实现更好的性能。EfficientNet 的设计思想是在网络的深度、宽度和分辨率方面进行均衡的调整,以获得高效且高性能的模型。 以下是 EfficientNet 的主要特点和设计原则&a…

VS2022创建控制台应用程序后没有Main了,如何显示Main?

文章目录 问题描述原因解决方案简单的顶级语句试用计算器 其他文章 问题描述 用VS2022创建一个控制台应用后,没有名称空间和Main函数了,只有一个WriteLine,如下所示。 // See https://aka.ms/new-console-template for more information Co…

今日学习 Mybatis 的关联映射

关联映射的三种关系: 我们首先绘制一个简化的 E-R 图来表示三种关联关系。 上图表示的三种关系: 一对一:一个班主任只属于一个班级,一个班级也只能有一个班主任一对多:一个班级有多个学生,一个学生只属于…

全网最细讲解如何实现导出Excel压缩包文件

写在前面的话 接下来我会使用传统的RESTful风格的方式结合MVC的开发模式给大家介绍一下如何去实现标题的效果。 基本思路讲解 先从数据库中查询出一组人员信息记录,保存在List list中。遍历这个列表,对于每一个人员信息,将其填充到一个Excel…

小白学Python:提取Word中的所有图片,只需要1行代码

#python# 大家好,这里是程序员晚枫,全网同名。 最近在小破站账号:Python自动化办公社区更新一套课程:给小白的《50讲Python自动化办公》 在课程群里,看到学员自己开发了一个功能:从word里提取图片。这个…

pytorch安装教程

写在前面:配置pytorch着实有很多坑,不过最终结果算好的,话不多说,直接上干货。其中想要知道如何解决torch.cuda.is_available()返回false的,直接跳到步骤5pytorch安装。python版本至少是3.6及以上。 1、前…

API 设计/开发/测试工具:Apifox与怎么通过拦截器

目录 一、测试接口如何创建? 二、如何添加body和header? 三、如果项目设置的有拦截器? 四、拦截器概念: 4.1使用拦截器概念 4.2 先写一个配置类WebMvcConfig.java 4.3 AuthInitInterceptor拦截器中实现 一、测试接口如何创建…

Linux 内存workingset Refault Distance算法源码及源码解析

概述 内核mm子系统中有一个workingset.c实现了refault distance算法,发现网络逻辑介绍该算法的文章主要是复制自奔跑吧内核一书中的内容,均比较雷同,讲述的角度比较难以理解,我第一看到的时候琢磨了2天才明白,本文希望…

Python中使用EMD(经验模态分解)

在Python中使用EMD(经验模态分解)进行信号分解时,通常可以设置信号分解的数目。EMD算法的目标是将信号分解成多个称为“本征模态函数”(Intrinsic Mode Functions,简称IMF)的成分,每个IMF都代表…