CTR模型在互联网的搜寻、举荐、广告等场景有着宽泛的利用。近年来,随着深度神经网络的引入,CTR模型的推理对硬件算力的要求逐步减少。本文介绍了美团在CTR模型优化的实际。通过分析模型构造特点,联合GPU硬件架构,咱们设计了一系列流程对模型进行定制优化,达到了升高提早、进步吞吐、节省成本的指标。

1 背景

CTR(Click-Through-Rate)即点击通过率,是指网络广告的点击达到率,即该广告的理论点击次数除以广告的展示量。为CTR指标服务的打分模型,个别称为CTR模型。咱们能够将此概念进一步扩大到互联网利用中各种预估转化率的模型。CTR模型在举荐、搜寻、广告等场景被广泛应用。绝对于CV(计算机视觉)、NLP(天然语音解决)场景的模型,CTR模型的历史构造比较简单,计算量较小。美团的CTR模型始终沿用CPU推理的形式。随着近几年深度神经网络的引入,CTR模型构造逐步趋于简单,计算量也越来越大,CPU开始不能满足模型对于算力的需要。

而GPU领有几千个计算外围,能够在单机内提供密集的并行计算能力,在CV、NLP等畛域展现了弱小的能力。通过CUDA[1]及相干API,英伟达建设了残缺的GPU生态。基于此,美团根底研发平台通过一套计划将CTR模型部署到GPU上。单从模型预测阶段看,咱们提供的基于英伟达T4的GPU深度优化计划,在雷同老本束缚下,比照CPU,晋升了10倍的吞吐能力。同时,在典型的搜寻精排场景中,从端到端的维度来看,整体吞吐能力晋升了一倍以上。

除了进步吞吐、降低成本外,GPU计划还为CTR模型的利用带来了额定的可能。例如,在某搜寻框主动补全的场景,因为人造的交互属性,时延要求十分刻薄,一般来说无奈应用简单的模型。而在GPU能力的加持下,某简单模型的均匀响应工夫从15毫秒升高至6~7毫秒,曾经达到了上线要求。

接下来,本文将与大家探讨美团机器学习平台提供的新一代CTR预测服务的GPU优化思路、成果、劣势与有余,心愿对从事相干工作的同学有所帮忙或者启发。

2 CTR模型GPU推理的挑战

2.1 应用层的挑战

  1. CTR模型构造多变,蕴含大量业务相干的构造,同时新的SOTA模型也层出不穷,硬件供应商因为人力受限,会重点优化罕用的经典构造,如ResNet。对于没有收敛的构造,官网没有端到端的优化工具能够反对。
  2. CTR模型中通常蕴含较大的Embedding表构造,要思考到Embedding表存在显寄存不下的状况。
  3. 在典型的举荐场景中,为了达到更快的POI曝光的目标,模型的时效性要求很高,在线模型服务须要提供增量更新模型的能力。

2.2 框架层的挑战

  1. 算子层面:目前支流的深度学习框架,如TensorFlow和PyTorch,能够说是深度学习第二代框架,它们首先要解决第一代框架Caffe的问题,Caffe有一个显著问题就是Layer的粒度过粗,导致那个时代的算法开发者都必须有“本人写自定义层”的能力。TensorFlow和PyTorch都把模型表达能力放在较高的优先级,导致算子粒度比拟小,无论是对CPU还是GPU架构,都会带来很大的额定开销。
  2. 框架层面:TensorFlow和PyTorch实质都是训练框架,对算法开发者比拟敌对,但非部署敌对。其中隐含了很多为了不便分布式训练做的设计,比方TensorFlow为了不便将Variable拆到不同的PS上,内置了Partitioned_Variable的设计。在基于GPU单机预测的场景下,这些构造也会带来额定的开销。

2.3 硬件层的挑战

第一,TensorFlow的算子粒度划分较细,导致一个模型通常由几千个算子形成,这些算子在GPU上的执行转变为对应的GPU kernel的执行。kernel是GPU上并行执行的函数。

GPU kernel大体上能够划分为传输数据、kernel启动、kernel计算等几个阶段,其中每个kernel的启动须要约10左右。大量的小算子导致每个kernel的执行工夫很短,kernel启动的耗时占了大部分。相邻的kernel之间须要通过读写显存进行数据的传输,产生大量的访存开销。而GPU的访存吞吐远远低于计算吞吐,导致性能低下,GPU利用率并不高。

第二,GPU卡上蕴含多个计算单元,实践上,不同计算单元是能够跑不同kernel的,但实际上为了编程简略,CUDA默认假如在同一时刻一个Stream里跑同一个kernel。尽管能够通过多Stream的形式跑,然而多Steam之间又短少细粒度的协同机制。

在通过充沛调研与探讨后,咱们决定第一期重点关注TensorFlow框架下如何解决常见CTR模型构造在英伟达GPU上执行效率不高的问题,咱们先将问题收敛为以下两个子问题:

  1. 算子粒度过细,GPU执行效率低下。
  2. 模型构造多变,手工优化投入大,通用性差。

3 优化伎俩

为了解决下面的问题,咱们对业界深度学习加速器进行了一些调研。业界比拟成熟的推理优化计划次要是TensorRT/XLA/TVM。TensorRT采纳手工优化,对一些定制的模型构造进行算子交融,并对计算密集型算子(如卷积)进行了高效调优。XLA是TensorFlow内置的编译优化工具,次要针对访存密集型构造,通过编译伎俩,实现算子的交融。TVM[2]具备较全面的优化能力,应用编译伎俩进行算子的交融,同时能够通过机器学习的形式实现计算密集型算子的主动调优。

通过宽泛的调研和比照,咱们最终抉择了TVM作为优化工具。TVM通过编译伎俩,能够较好地应答多变的模型构造,解决了手工优化通用性差的问题。但TVM利用在业务模型也存在一系列问题:反对的算子数较少,而且目前对动静Shape的反对还不够好。针对这两个问题,咱们将TVM和TensorFlow联合起来,联合CTR模型的构造特点与GPU的硬件个性,开发一系列流程,实现了对CTR模型的优化。

3.1 算子交融

通过将多个小算子交融为一个语义等价的大算子,能够无效缩小GPU上的kernel数量。一方面,kernel数量缩小间接升高了kernel发射的开销;另一方面,交融后的大kernel执行的计算量减少,防止了多个kernel间数据传输导致的频繁访存,进步了计算的访存比。

能够看到,上图中的左右等价构造,左侧的21个算子执行的运算,能够在1个等价算子中实现。反映到GPU的流动上,左侧至多有21个GPU kernel以及21次显存的读写,而右侧只须要执行1个kernel以及1次显存读写。对于每个交融后的算子,须要有对应的kernel实现。然而,模型的算子组合是无穷的,对每种交融后算子手工实现kernel是不事实的。TVM通过编译伎俩,能够主动进行算子的交融以及设施代码生成,防止了逐个手写kernel的累赘。

3.1.1 TF-TVM主动切图优化

TensorFlow模型中,如果蕴含TVM不反对的算子,会导致无奈执行TVM转换。咱们的思路是将能够用TVM优化的局部切出来,转为TVM的engine,其余局部仍然应用TensorFlow的算子。在XLA和TRT转换的时候也有相似问题,咱们剖析了TF-XLA和TF-TRT二者的实现:

  1. TF-XLA的实现计划,在Grappler[4]优化图之后,有一个POST_REWRITE_FOR_EXEC(通过这个关键字能够在源码中搜寻到)阶段,在这个阶段,会执行三个针对Graph的Pass,别离是用来标记算子,封装子图,改写子图并构建LaunchOp。
  2. TF-TRT的实现计划,TF-TRT在Grappler中注册了一个优化器,在这个优化器中,找到连通子图,并将其替换为TRT Engine。

在最终计划实现上,咱们参考了TF-TRT的设计。这个设计比照XLA的劣势在于XLA切图计划与TensorFlow源码紧耦合,间接将XLA的三个Pass嵌入到了启动Session的主流程中。而切图策略,优化策略后续会有十分频繁的迭代,咱们不心愿与TensorFlow的源码太过耦合。咱们扩大了TF-TVM的计划,在理论应用中咱们把这个切图过程为一个独立流程。在模型部署或更新时,主动触发。

在推理阶段,优化过的子图应用TVM执行,其余的计算图应用TensorFlow原生实现执行,将两者联合共同完成模型的推理。因为TVM和TensorFlow的Runtime各自应用独立的内存治理,数据在不同框架间传输会导致额定的性能开销。为了升高这部分开销,咱们买通了两个框架的底层数据结构,尽可能防止额定的数据拷贝。

3.1.2 计算图等价替换

TensorFlow模型中过多的不被TVM反对的算子会导致TF-TVM切图系统,影响最终的优化成果。为了让TF-TVM切图尽量大且残缺,以及让TVM优化过程中的交融力度更大,咱们对模型中的一些简单构造进行检测,替换为执行更高效或更易于交融的等价构造。

例如,TensorFlow原生EmbeddingLookup构造,为了反对分布式训练,会对Embedding表进行切分,产生DynamicPartition和ParallelDynamicStitch等动静算子。这些动静算子不被TVM反对,导致TF-TVM图切分过于细碎。为了让TF-TVM切图更残缺,咱们通过图替换,对这种构造进行批改,通过将Embedding分表提前合并,失去简化的EmbeddingLookup构造。

3.2 CPU-GPU数据传输优化

TVM优化后的子图被替换为一个节点,该节点在GPU上执行,通常有几十甚至几百个输出,该节点的前置输出(如Placeholder)通常是在CPU上执行,会波及屡次的CPU-GPU传输。频繁的小数据量传输,无奈充分利用带宽。为了解决这个问题,咱们对模型构造进行批改,在计算图中增加合并与拆分节点,管制切图的地位,缩小数据传输的次数。

一种可能的合并形式是,对这些输出按雷同的Shape和Dtype进行合并,后续进行拆分,将拆分节点切入TVM的子图一起优化。这种形式会导致一些问题,如局部子图的算子交融成果不佳;另一方面,GPU kernel函数的参数传递内存限度在4KB,对于TVM节点输出十分多的状况(如超过512个),会遇到生成代码不非法的状况。

3.3 高频子图手工优化

对于TVM无奈反对的子图,咱们对业务中高频应用的构造进行形象,采纳手写自定义算子的形式,进行了高效GPU实现。

例如,模型中有局部时序特色应用String类型输出,将输出的字符串转为补齐的数字Tensor,将int类型的Tensor作为下标进行Embedding操作。这部分子图的语义如图,以下简称SE构造(StringEmbedding):

这一部分构造,TensorFlow的原生实现只有基于CPU的版本,在数据量较大且并行度较高的情景下,性能降落重大,成为整个模型的瓶颈。为了优化这部分构造的性能,咱们在GPU上实现了高效的等价操作。

如图所示,PadString算子在CPU端将多个字符串按最大长度进行补齐,拼接成一个内存间断的uint8类型Tensor,以便一次性传输到GPU。StringEmbedding接管到补齐后的字符串后,利用GPU并行计算的个性,协同大量线程实现字符串的切分与查表操作。在波及规约求和、求前缀和等要害过程中,应用了GPU上的Reduce/Scan算法,编码过程应用warp_shuffle指令,不同线程通过寄存器替换数据,防止了频繁访存的开销,取得了很好的性能。

GPU Scan算法示意,一个8个元素的前缀和操作,只须要3个迭代周期。在一个有几十路相似操作的模型中,手工优化前后的GPU timeline对比方下图,能够看到H2D + StringEmbedding这部分构造的耗时有很大的缩减,从42毫秒缩减到1.83毫秒。

除了StringEmbedding构造,咱们对StringSplit + ToNumber + SparseSegmentSqrt、多路并行StringEmbedding等构造都进行了高效交融实现,在优化流程中通过构造匹配进行相应的替换。

3.4 CPU-GPU分流

理论线上的RPC申请,每个申请内的样本数(下文称Batch)是在[1,MaxValue]范畴内变动的,MaxValue受上游业务零碎,其余根底零碎能力等多方面因素制约,绝对固定。如上图所示,以某个搜寻服务为例,咱们统计了线上的Batch数值散布,Batch=MaxValue的申请占比约45%,Batch=45占比7.4%,Batch=1占比2.3%。其余的Batch占比从0.5%到1%不等。对于GPU来说,进步单个申请的Batch能更好地利用硬件资源,施展GPU的并行计算能力,体现出绝对CPU更优的提早和吞吐;当Batch较小时,GPU绝对CPU的劣势就不显著了(下图是咱们测试同样的模型在固定压力下,CPU/GPU上提早的变动)。

大部分申请都由GPU在做了,CPU资源有较多空余,咱们将一些小Batch的碎申请放在CPU运行,这样能够让整个Worker的资源利用更加平衡,进步零碎整体的性能。咱们依据测试设定了一个Batch阈值,以及计算图在异构硬件上区别执行的判断逻辑:对于小Batch的状况,间接在CPU上执行计算图,只有Batch超过阈值的申请才会在GPU上推理。从线上的统计数据来看,整体流量的77%跑在GPU上,23%跑在CPU上。

在GPU的一系列优化策略和动作中,Batch大小是很重要的信息,不同Batch下优化出的kernel实现可能是不同的,以达到对应workload下最优的计算性能;因为线上的流量特点,发送到GPU的申请Batch散布比拟细碎,如果咱们针对每个Batch都优化一个模型的kernel实现显然是不够经济和通用的。因而,咱们设计了一个Batch分桶策略,生成N个固定Batch的优化模型,在理论申请到来时找到Batch间隔最近的一个Bucket,将申请向上Padding到对应的Batch计算,从而进步了GPU的利用效率。

4 压测性能剖析

咱们选取一个模型进行线上性能压测剖析。

  • CPU模型测试环境为16核Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz,16G内存。
  • GPU模型测试环境为8核Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz,Tesla T4 GPU,16G内存。

下图比照了在不同的QPS下(x轴),GPU模型在各BatchSize下的推理时延(y轴)。GPU模型在BatchSize=128以下,推理耗时差别不显著,较大的BatchSize更有利于吞吐;比照BatchSize=256的GPU模型与BatchSize为25的CPU模型,在QPS低于64的状况下,二者推理耗时根本持平;QPS超过64的状况下,GPU的推理时延低于CPU。GPU的吞吐相比CPU晋升了10倍。

同时,咱们能够看到不同曲线的平缓水平,CPU在QPS高出64后,时延会迅速回升,GPU则仍然放弃安稳,直到QPS超过128才会有显著回升,但仍旧比CPU更安稳。

5 整体架构

针对CTR模型的构造特点,咱们形象出了一套平台化的通用优化流程。通过对模型构造的剖析,主动利用适合的优化策略,通过性能评估和一致性校验,保障模型的优化成果。

6 不足之处与将来布局

在易用性层面,目前的计划模式是提供了一套在线优化脚本,用户提交模型后,主动优化部署。因为波及对计算图构造的剖析、编辑以及TVM的编译等过程,目前的模型优化耗时较长,大部分模型优化耗时在20分钟左右。后续须要思考减速TVM编译的效率。

在通用性层面,从咱们的理论利用状况来看,TVM编译优化和高性能手写算子是最次要的收益起源。手工优化很考验开发同学对业务模型的了解和GPU编程的能力。编写一个高性能的交融算子曾经不太容易,要做到有肯定的迁徙能力和扩展性则更有难度。

总的来说,CTR模型推理在GPU上将来须要思考的问题还有很多。除了要基于业务了解提供更好的性能外,还要思考模型规模微小后无奈残缺放入显存的问题以及反对在线模型更新的问题。

作者简介

伟龙、小卓、文魁、駃飞、小新等,均来自美团根底研发平台-机器学习预测引擎组。

参考资料

[1] CUDA C++ Programming Guide
[2] TVM Documentation
[3] Accelerating Inference In TF-TRT User Guide
[4] TensorFlow graph optimization with Grappler

招聘信息

美团机器学习平台大量岗位继续招聘中,实习、社招均可,坐标北京/上海,欢送感兴趣的同学退出咱们,构建多畛域公司级机器学习平台,帮大家吃得更好,生存更好。简历可投递至:wangxin66@meituan.com。

浏览美团技术团队更多技术文章合集

前端 | 算法 | 后端 | 数据 | 平安 | 运维 | iOS | Android | 测试

| 在公众号菜单栏对话框回复【2020年货】、【2019年货】、【2018年货】、【2017年货】等关键词,可查看美团技术团队历年技术文章合集。

| 本文系美团技术团队出品,著作权归属美团。欢送出于分享和交换等非商业目标转载或应用本文内容,敬请注明“内容转载自美团技术团队”。本文未经许可,不得进行商业性转载或者应用。任何商用行为,请发送邮件至tech@meituan.com申请受权。