Skip to content

nono-Sang/Max-Code

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Max-Code

初赛题目简介

广告机器学习服务平台每隔一段时间会产出一个较大的模型数据(几十或上百 GB),并且将其存储于 HDFS 中。要求设计一个分布式服务,能够加载模型数据,提供在线访问模型数据的能力,在新的模型数据产出时,服务自动加载使用新的模型数据,同时支持数据回滚能力,即服务加载指定版本的模型数据。

简要思路

关键点:单个节点的内存无法存下整个模型数据。如果当请求到来时从磁盘加载数据,时延会很高。因此,将模型数据进行划分,各个节点加载其中的一部分到自己的内存中。请求到来时,节点从本地内存以及其他节点获取数据(同一集群下,网络速度快于磁盘速度)。也就是说,每个节点需要提供两个服务,一个接收用户的请求,另一个接收其他节点的请求(主要是 RPC 的使用与优化)。

更新与回滚:每个节点均使用一个线程周期性地向 HDFS 查询是否有新模型或者回滚需求产生。节点之间的同步借助 ETCD 来实现,简而言之,当本地加载数据完毕后(假设模型名为 model_v1,节点ID为 x),节点调用 etcd.set("/model_v1/x"),然后不断查询 "/model_v1/" 目录下的元素数量,直到达到节点总数量为止,意味着所有节点均已加载完毕,分布式服务可以使用新模型数据。

内存优化:事实上,任意时间点,节点内存中最多只用存储两个模型的数据。例如,初始时加载 model_v1,之后出现新模型 model_v2(注意在加载过程中服务不能断开,model_v1 的数据仍然会被使用),然后回滚到 model_v3(加载之前可以释放或者复用 model_v1 的内存)。

加载优化:模型由许多切片组成,赛题要求加载时,数据先从 HDFS 到本地磁盘(步骤1),然后再到内存(步骤2)。因此,可以让线程1执行步骤1,线程2执行步骤2,并且借助 promise/future 来进行同步,即线程1每把一个切片放到磁盘,线程2便可立即将其载入内存,从而实现流水线式的加载过程。

复赛题目简介

基于 TensorFlow 的多层 DNN 模型的推理优化

简要思路

提高并发:使用 virtual GPU(即多个 CUDA Stream)。由于推理的计算并不重,加上访存密集型算子的存在,GPU 会存在很多空闲状态。我们通过多个 CUDA Stream 来提升系统的并发性,让 GPU 承担更多的计算负载。

开启 XLA:XLA 是 TensorFlow 自带的用于访存密集型算子的编译器优化引擎。其工作方式大概为:将计算图转为 HLO IR,然后找到可以融合的算子,根据融合模式生成融合 kernel 并进行缓存,这里的融合模式是包含完整 shape 信息的算子序列,所以 XLA 遇到一个融合模式时,首先判断其是否已经缓存,如果没有,才进行编译和缓存。所以,在动态 shape 的场景下,大量的重编译和缓存开销会导致 XLA 不可用。考虑到输入数据的 batchsize 是稳定分布的,我们通过分桶 padding 的方式来解决这个问题。

混合精度:TensorFlow 会将算子分为三类,分别是:(1) Always 算子,确定可以通过 FP16 来加速。(2) Maybe 算子,加速能否抵过数据转换的开销是不确定的。(3) Never 算子,必须使用 FP32 来保证数值稳定性。TensorFlow 通过图转换来完成混合精度,过程大概为:(1) 初始化算子标签;(2) 将被 Never 算子包围的算子的标签修改为 Never;(3) 将被 Always 算子包围的 Maybe 算子的标签修改为 Always;(4) 确定 Always 算子的边界,插入 FP16 或 FP32 Cast 算子。

图优化:对计算图做一些等价变换,我们在输入节点后插入 DeepCopy 算子来充当 Pinned Memory,以实现零拷贝异步数据传输。

About

2023 Max-Code比赛代码

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published