爱奇艺 CTR 场景下的 GPU 推理性能优化

01

   背景介绍

GPU 目前大量应用在了爱奇艺深度学习平台上。GPU 拥有成百上千个处理核心,能够并行的执行大量指令,非常适合用来做深度学习相关的计算。在 CV(计算机视觉),NLP(自然语言处理)的模型上,已经广泛的使用了 GPU,相比 CPU 通常能够更快、更经济的完成模型的训练和推理。

CTR (Click Trough Rate) 模型广泛使用在推荐、广告、搜索等场景中,用来估算用户点击某个广告、视频的概率。在 CTR 模型的训练场景中已经大量使用了 GPU,在提升训练速度的同时和降低了所需的服务器成本。

但在推理场景下,当我们直接把训练好的模型通过 Tensorflow-serving 部署在 GPU 之后,发现推理效果并不理想。表现在:

  1. 推理延迟高,CTR 类模型通常是面向终端用户的,对于推理延迟非常敏感。

  2. GPU 利用率低,计算能力未能全部发挥出来。


02

   原因分析

分析工具

  1. Tensorflow Board,tensorflow 官方提供的工具,能够可视化的查看计算流图中各个阶段的耗时,并汇总算子的总耗时。

  2. Nsight 是 NVIDIA 面向 CUDA 开发者提供的开发工具套件,能够对 CUDA 程序进行相对底层的跟踪、调试和性能分析。

分析结论

典型的 CTR 模型输入,包含大量的稀疏类特征(如设备 ID、最近浏览视频 ID 等)。Tensorflow 的 FeatureColumn 会对这些特征进行处理,首先进行 identity/hash 操作,得到 embedding table 的 index。再经 embedding lookup 和求均值等操作后,得到对应的 embedding tensor。多个特征对应的 embedding tensor 拼接后得到一个新的 tensor,再进入后续的 DNN/Transformer 等结构。

因此每个稀疏特征在模型的输入层,都会启动若干个算子。每个算子会对应着一次或者几次 GPU 计算,即 cuda kernel。每个 cuda kernel 包括两个阶段,launch cuda kernel(启动 kernel 所必需的 overhead) 和 kernel 执行(在 cuda 核心上真正执行矩阵计算)。稀疏特征 identity/hash/embedding lookup 对应的算子计算量较小,launch kernel 的耗时往往超过 kernel 执行的时间。一般来说 CTR 模型包含了几十到几百个稀疏特征,理论上就会有数百次 launch kernel,是当前主要的性能瓶颈。

在使用 GPU 训练 CTR 模型时,没有遇到这个问题。因为训练本身是一个离线任务,不关注延迟,所以训练时候的 batch size 都可以很大。虽然仍会进行多次 launch kernel,只要执行 kernel 时候计算的样本数量足够多,lauch kernel 的开销平均到每个样本上的时间就很小了。而对于在线推理的场景,如果要求 Tensorflow Serving 收到足够的推理请求并合并批次后再进行计算,那么推理延迟就会很高。


03

   优化方案

我们的目标是在基本不改变训练代码,不改变服务框架的前提下,进行性能优化。我们很自然的想到两个方法,减少启动的 kernel 数量,提高 kernel 启动的速度。

算子融合

基本操作就是将多个连续的操作或算子合并成一个单一的算子,一方面可以减少 cuda kernel 启动的次数,另一方面可以把计算过程中一些中间结果存在寄存器或者共享内存,只在算子的最后把计算结果写入全局的 cuda 内存。

主要有两种方法

  1. 基于深度学习编译器的自动融合

  2. 针对业务的手动算子融合

自动融合

我们尝试了多种深度学习编译器,如 TVM/TensorRT/XLA,实测可以实现 DNN 部分少量算子的融合,如连续的 MatrixMat/ADD/Relu。由于 TVM/TensorRT 需要导出 onnx 等中间格式,需要修改原有模型的上线流程。所以我们通过 tf.ConfigProto() 开启 tensorflow 内置的 XLA 来进行融合。

但自动融合对稀疏特征相关的算子并没有很好的融合效果。

手动算子融合

我们很自然的想到,如果有多个特征在输入层被相同类型的 FeatureColumn 组合所处理,那么我们可以实现一个算子,把多个特征的输入拼接成数组作为算子的输入。算子的输出是一个张量,这个张量的 shape 和原本多个特征分别计算后再拼接得到的张量 shape 一致。

以原有的 IdentityCategoricalColumn + EmbeddingColumn 组合为例,我们实现了 BatchIdentiyEmbeddingLookup 算子,达到相同的计算逻辑。

为了方便算法同学使用,我们封装了一个新的 FusedFeatureLayer,来代替原生的 FeatureLayer;除了包含融合算子,还实现了以下逻辑:

  1. 融合的逻辑在推理时候生效,训练时候走原来的逻辑。

  2. 需要对特征进行排序,保证相同类型的特征可以排在一起。

  3. 由于每个特征的输入均为变长,在这里我们额外生成了一个索引数组,来标记输入数组的每个元素属于哪个特征。

对于业务来说,只需要替换原来的 FeatureLayer 即可达到融合的效果。

实测原本数百次的 launch kernel,经过手动融合后缩减到了 10 次以内。大大减少了启动 kernel 的开销。

aeba3d6703e6f86c9979d3912d02cb4f.png

4448272748297e33d6e73fc87ef3422f.png


MultiStream 提高 launch 效率

TensorFlow 本身是一个单流模型,只包含一个 Cuda Stream Group(由  Compute Stream、H2D Stream,D2H Stream 和 D2D Stream 组成)多个 kernel 只能在同一个 Compute Stream 上串行执行效率较低。即使通过多个 tensorflow 的 session 来 launch cuda kernel,在 GPU 侧仍然需要排队。

980c461eacbcfedb42ff4d57fb626465.jpeg

为此 NVIDIA 的技术团队维护了一个自己的 Tensorflow 分支,支持多个 Stream Group 同时执行。以此来提高 launch cuda kernel 的效率。我们将此特性移植到了我们的 Tensorflow Serving 里。

0989e9c5373c5da4ca9b71cc9d89f51a.png

在 Tensorflow Serving 运行时候,需要开启 Nvidia MPS,减少多个 CUDA Context 间的相互干扰。

小数据拷贝优化

在前边优化基础上,我们针对小数据拷贝进一步做了优化。当 Tensorflow Serving 从请求中反序列化出中各个特征的值后,会多次调用 cudamemcpy,将数据从 host 拷贝到 device。调用次数取决于特征数量。

大部分 CTR 类业务,实测当 batchsize 较小时和,先将数据在 host 侧拼接,再一次性的调用 cudamemcpy 效率会更高一些。

5279ede055094522af8abb1811229534.png


合并批次

GPU 场景下需要开启批次合并。默认情况下 Tensorflow Serving 是不对请求进行合并的。为了更好的利用 GPU 的并行计算能力,让一次前向计算时候可以包含更多的样本。我们在运行时候打开了 Tensorflow Serving 的 enable_batching 选项,来对多个请求进行批次合并。同时需要提供一个 batch config 文件,重点配置以下参数,以下是我们总结的一些经验。

  1. max_batch_size:一个批次允许的最大请求数量,可以稍微大一点。

  2. batch_timeout_micros:合并一个批次等待的最长时间,即使该批次的数量未达到max_batch_size,也会立即进行计算(单位是微秒),理论上延迟要求越高,这儿设置的越小,最好设置在 5 毫秒以下。

  3. num_batch_threads:最大推理并发线程,在开启了 MPS 之后,设置成 1 到 4 都可以,再多延迟会高。

在这里需要注意的是,CTR 类模型大部分输入的稀疏特征都为变长特征。如果客户端没有专门做约定,可能出现多个请求中在某个特征上的长度不一致。Tensorflow Serving 有一个默认的 padding 逻辑,给较短的请求在对应的特征上补 0。而对于变长特征使用 -1 来表示空,默认的补 0 会事实上改变原有的请求的含义。

比如用户 A 最近的观看视频 id 为 [3,5],用户 B 最近的观看视频 id 为 [7,9,10]。如果默认补齐,请求变成 [[3,5,0], [7,9,10]],在后续的处理中,模型会认为 A 最近观看了 id 为 3,5,0 的 3 个视频。

因此我们修改了 Tensorflow Serving 响应的补齐逻辑,遇到这种情况会补齐为 [[3,5,-1], [7,9,10]]。第一行的含义仍然是观看了视频 3,5。

04

   最终效果

经过各种上述各种优化,在延迟和吞吐量满足了我们的需求,并落地在推荐个性化 Push、瀑布流业务上。业务效果如下:

  1. 吞吐量相比原生Tensorflow GPU 容器提升 6 倍以上

  2. 延迟和 CPU 基本一致,满足业务需求

  3. 支持相同的 QPS 时候,成本降低 40% 以上

7c711e47f79ea1fa32e962fe4a8ae91b.jpeg

也许你还想看

爱奇艺数据湖实战 - Hive数仓平滑入湖

稀疏大模型在爱奇艺广告排序场景中的实践

爱奇艺图片格式演进

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

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

相关文章

基于SpringBoot SSM vue办公自动化系统

基于SpringBoot SSM vue办公自动化系统 系统功能 登录 个人中心 请假信息管理 考勤信息管理 出差信息管理 行政领导管理 代办事项管理 文档管理 公告信息管理 企业信息管理 会议室信息管理 资产设备管理 员工信息管理 开发环境和技术 开发语言:Java 使用框架: S…

ChatGLM3-6B独立部署提供HTTP服务failed to open nvrtc-builtins64_121.dll

背景 我在本地windoes部署ChatGLM3-bB,且希望部署后能提供HTTP server的能力。 模型部署且启动是成功了,但是在访问生成接口/v1/chat/completions时报错failed to open nvrtc-builtins64_121.dll。 问题详细描述 找不到nvrtc-builtins64_121.dll Runtime…

【JavaScript】JavaScript 运算符 ④ ( 逻辑运算符 | 逻辑与运算符 | 逻辑或运算符 || | 逻辑非运算符 ! )

文章目录 一、JavaScript 逻辑运算符1、逻辑运算符 概念2、逻辑与运算符 &&3、逻辑或运算符 ||4、逻辑非运算符 !5、完整代码示例 一、JavaScript 逻辑运算符 1、逻辑运算符 概念 JavaScript 中的 逻辑运算符 的作用是 对 布尔值 进行运算 , 运算完成 后 的 返回值 也是…

2.26回顾章节主体线索脉络,课程要求(评分)

3)翻译程序、汇编程序、编译程序、解释程序有什么差别?各自的特性是什么? 翻译程序是指把高级语言源程序翻译成机器语言程序(目标代码)的软件。 翻译程序有两种:一种是编译程序,它将高级语言源程序一次全部…

学习笔记--强化学习(1)

参考:https://blog.csdn.net/koulongxin123/article/details/122676149 1.什么是强化学习? (1)定义 基于环境的反馈而行动,通过不断与环境的交互、试错,最终完成特定目的或者使得整体行动收益最大化(是一种通过与环境…

CKA认证之Etcd备份与恢复

题目介绍: 资料参考: https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/configure-upgrade-etcd 解题: 1、备份 #参考模板列出 etcdctl 可用的各种选项。 #例如,你可以通过指定端点、证书和密钥来制作快照&#xff0…

15 个最佳免费照片恢复软件快速恢复已删除的图像

这篇文章重点介绍了适用于 Windows 10 的 15 款最佳免费照片恢复软件。阅读整篇文章,了解理想的图像恢复软件。 照片可以带回所有的回忆,回忆起与我们所爱的人和亲密的人度过的每一个“时刻”。照片是我们永远不想失去的东西,但有时我们会无…

【热门话题】前端框架发展史

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 前端开发的历史演变引言第一章:起源与基础建设 - HTML与CSS时代1.1 …

THM学习笔记—RootMe

nmap扫描,发现22端口和80端口打开 dirsearch扫描,注意到/panel和/uploads,在浏览器中打开 可以上传文件,尝试反弹shell 在尝试过程中发现网站不能上传.php文件,只需要将后缀更改为.php5之类即可 成功 查找文件&#x…

前世档案(不用二叉树语法秒杀版c++)

网络世界中时常会遇到这类滑稽的算命小程序,实现原理很简单,随便设计几个问题,根据玩家对每个问题的回答选择一条判断树中的路径(如下图所示),结论就是路径终点对应的那个结点。 现在我们把结论从左到右顺序…

Java面试题总结18之springcloud四种分布式事务解决方案

XA规范:分布式事务规范,规定了分布式事务模型 四个角色:事务管理器(协调者TM),资源管理器(参与者RM),应用程序AP,通信资源管理器CRM 全局事务:一…

一款博客网站源码

一款博客网站源码 源码软件库 为大家内置了主题 清爽又强大真正的永久可用的一条源码,该版本为整合版本,内置了Joe主题,搭建后直接启用即可~ 安装环境要求: PHP 7.2 以上 MySQL, PostgreSQL, SQLite 任意一种数据库支持&#xff…

【LeetCode热题100】148. 排序链表(链表)

一.题目要求 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 二.题目难度 中等 三.输入样例 示例 1: 输入:head [4,2,1,3] 输出:[1,2,3,4] 示例 2: 输入:head [-1,5,3,4,0] 输…

【Java基础】IO流(三):字符流的FileReader(文件字符输入流)和 FileWriter(文件字节输出流)

目录 字符流 1、FileReader(字符输入流) 1.1、无参的read( )方法示例 ​编辑 1.2、有参的read(char[ ] buffer)方法示例 2、FileWriter(字符输出流) 字符流 字符流的底层其实就是字节流,即字符流 字节流 字符集…

基于ESTAR指数平滑转换自回归模型的CPI数据统计分析matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 ESTAR模型概述 4.2 WNL值,P值, Q值,12阶ARCH值 4.3ADF检验 5.完整程序 1.程序功能描述 基于ESTAR指数平滑转换自回归模型的CPI数据统计分析matlab仿…

LeetCode Python - 58. 最后一个单词的长度

目录 题目描述解法运行结果 题目描述 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1: 输入:s “Hel…

【Vue】Request模块 - axios 封装Vuex的持久化存储

📝个人主页:五敷有你 🔥系列专栏:Vue ⛺️稳中求进,晒太阳 Request模块 - axios 封装 使用axios来请求后端接口,一般会对axios进行一些配置(比如配置基础地址,请求响应拦截器…

HCIP—BGP邻居关系建立实验

BGP的邻居称为:IBGP对等体 EBGP对等体 1.EBGP对等体关系: 位于 不同自治系统 的BGP路由器之间的BGP对等体关系 EBGP对等体一般使用 直连建立 对等体关系,EBGP邻居之间的报文 TTL中值设置为1 两台路由器之间建立EBGP对等体关系&#xff0…

vue3新功能-Teleport

1.teleport 在组件内的任何位置渲染内容 将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。 例:将组件dialog添加到body下面 <teleport to"body"> <el- dialog --> </teleport> 2.fragments 多个根元素外层不需要…

遵循Web标准规范,构建优质网页

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…