全网最佳IP代理服务商- 9.9元开通-稳定的代理服务
如果您从事外贸、海外视频博主、海外推广、海外广告投放,欢迎选择我们。
让您轻易使用国外主流的聊天软件、视频网站以及社交网络等等

clashheroes

clashheroes

最近在做广告买量系统,也就是游戏的新用户获取,在广告的分工中,我们是广告主的角色。基本的工作是从腾讯游戏积累的数据中获得用户的画像,并和广告媒体,游戏本身的特征相结合;通过深度学习的方法,计算其CVR(转化率)和LTV(长期用户价值),实时地对媒体的广告请求进行个性化出价,返回用户感兴趣的广告素材,在媒体端展现;从而引发用户兴趣,点击相应的广告以触发下载和注册。

在这个大数据的应用中,业界常用的基本都是基于open source和公司自研的一些工具链,例如kafka, spark, tdw, clickhouse,redis, tf-serving等。通过这些工具以及二次开发,可以搭建一套相对比较完整的深度学习应用平台。这个平台中的大部分可以运行地不错,但也会存在一些卡点或值得优化的地方:例如

广告系统对于实时性(在线ms)和实效性(模型更新,调价控制等)要求很高。很多时候不能通过简单的硬件扩容来解决问题,需要我们根据具体的业务需求做出相应的优化。本文探讨了广告买量系统中一些卡点的优化方案,设定优化的基本原则是

但随着计算机硬件的进步,特别是3大组件多核cpu,大内存,ssd的进步,很多以前在并行计算平台上面的工作,现在可以在单机完成。例如目前使用的一台IDC服务器的配置:

尤其是ssd存储的进步,在海量数据处理上面可以探索一些新的实现方法。当然这种方法适合于优化重要的卡点,而不是提供一个通用的解决方案。

SSD的4KB随机IOPS是SATA磁盘的200倍,顺序读写能力提升了18-20倍。同时需要充分利用多核和大内存。

有了一台性能比较好的服务器,如何合理地利用它的三大组件,关键在于如何并行化。也就是说需要把大文件分成适度大小的数据块,然后业务线程(多线程)可以同时处理多个数据块,从而达到并行的目的。同时还需要考虑数据生成写存储的问题。

把一个比较大的文本文件按照指定的大小Length(例如4-16MB)切分,但由于单条记录是不定长的,如果完全限定了块长度,则会出现截断记录的问题,会对业务线程处理造成比较大的麻烦。因此文件切分需要考虑记录的分割。

除了第一个块以外,读取其余所有block前部64KB的数据,查找第一个行分隔符,计算每个块的读取的偏移,长度;实际数据的偏移和实际的长度。 这部分数据的读取可以使用aio来并发读取数据。

采用预申请消息的模式,减少内存的申请和释放,同时由于使用了aio和DIRECT模式打开文件,某些类型的消息需要使用memalign来申请内存。

从fio测试的数据可以看出如果使用aio模式,读线时,就基本能达到顺序读带宽的上限,读到的数据放入消息SAioBlockMsg中。如果空闲read队列中没有消息,就等待;防止读效率大于业务线程效率时把内存消耗完。数据块读完成后,线程就主动退出。

不同的业务需要重写相应的回调函数。可以在业务处理中极致优化代码。业务线程需要等待主线程的发出的退出消息。 业务线程无需涉及文件的读和写,大大简化了业务逻辑的开发。

如果使用非direct io模式,会产生大量的系统缓存。但如果要使用direct io模式,在write的时候需要数据对齐,collect线程收集结果数据,并根据block_id保序,然后从空闲write队列中申请消息后,写入合并对齐的数据。

采用了direct i/o模式写入文件,配置输出文件时,为了防止读写相互干扰,最好和输入文件位于不同的磁盘

这个基本的框架,可以解决70%-80%的并行处理问题,特别的业务逻辑,大体也可以按照类似线程任务分解的方法处理。

使用MKL的库以后,除非有特别的需求,都无需使用immintrin来自己调用相关的avx指令。

mkl会自动根据不同cpu的指令集,调用根据指令集优化后的代码来加速,包括AMD的cpu。例如

目前我们项目组的大部分工作流程是训练好模型后,然后在线上使用tf servering c++clash运行中0bytes。一个相对简单的模型,平均单核的QPS在1000左右。这个QPS并不高。开源有很多针对tf serving的优化,一般方法集中在优化图计算。例如分析每个operator的输入和输出,将没有关联的operator并行化。但由于tf的图表示特别复杂,整体执行的效率偏低,优化的效果差强人意。特别是引入了feature column后,造成执行效率进一步下降。

从inference的需求而言,尤其是线上系统,更加关注性能、时延、稳定性;同时inference不涉及复杂的梯度下降和迭代,因此很多团队会考虑使用自定义的c/c++来优化线上的Inferece,大部分都采用了类似caffe()的方法。也就是c++加配置文件的方式来提升线上inference的性能。有不少的团队做过类似的工作,包括TEG的团队,百度的团队等。

负责读取配置文件,创建layer,根据配置的顺序,一步一步地执行整个模型。

每个layer的输入和输出在读取配置时需要严格判断,因为整个计算过程是一个有向图,当前的输入依赖于前向节点的输出。

每个layer最重要的几个属性:输入、输出、自有参数。通过输入和输出将整个计算流程串起来。

毫无疑问,通过c/c++和mkl可以使inference的性能有很大的提升,往往是原始tf serving的10倍以上。 但如果每次新建一个模型或修改后,都需要手工去写json的配置文件,造成额外的工作量和人为错误,无疑会被算法和运维同学抛弃。为了更方便地使用高性能的inferece系统,自动化配置生成工具必不可少。以下的方法仅仅针对tensorflow有效,因为tensorflow在保存模型时会生成静态的计算图;而pytorch需要动态计算,无法方便地获得完整的计算图。

feture column是tf引入的模块以方便处理特征clash苹果可以用吗,算法同学可以很容易地使用feture column和自定义的lambda函数很方便地处理原始特征

但不幸的是tf把feature column视作了算子(operator)来组织和计算,并且把lambda相关的计算都编译成了中间码保存。随着特征数量的不断增加,feature column的处理上面的时间消耗会越来越多。这是tf servering 性能低下的另一个重要原因。

回到feature column的原始定义上来,既然是特征处理,则应该把特征从模型处理中剥离出来,由专用的程序处理特征,然后把处理好的特征传递给模型直接训练或预测就行。例如一条原始的csv记录:每个特征使用,做分隔,子特征使用分隔。

这个记录包含了576个特征,某个模型使用了其中的514个特征,然后需要将其处理成为一个773维的样本。

既然能够从tf model的模型文件中解析出模型,那么能解析出feature column呢?答案是部分的:embedding可以解析(包括其中的vacabulary和weight),但如果numeric中使用了lambda,具体的内容将无法解析。此时需要在算法源码中给lambda一个名字,通过名字的约定来提供不同的numberic处理逻辑。

这样修改model_pedict.py程序,将tf的模型解析为两个部分,一个是model部分,另一个是feature_column部分。预测程序增加专门处理feature column的部分,将逐个将每个特征记录转换为float数组,然后调用model_predict进行预测。

项目组需要根据自己的需求自己实现不同的算子,包括feature column的不同处理方式。

在我们系统原有的设计中,涉及到的大量查询基本都使用了TRedis(km.woa.com/group/545/articles/show/294044?from=iSearch&sessionKey=vXaUuqri466cI7iQ7aKTFYPJmxqHHsuR) ,包括账号映射,状态查询,特征拼接等。仔细分析了一下,发现有较大一部分数据是“只读”的,或者说更新频度非常低(一天一次或一周一次,例如几个特征数据)。针对只读类型的查询,特别是在内存消耗上,还是值得做优化的。 年初在km上面发表了(km.woa.com/group/34294/articles/show/452556) ,遗留了两个问题,1是在线是内存的进一步优化。后来和AMS的zlinzlin同学交流,他在索引压缩上面做了很好的工作(km.oa.com/group/18268/articles/show/401644) 。因此进一步优化了hash查询,让它不仅仅可以适用于离线拼接,也适用于在线查询,同时也优化了内存的使用。

我们知道key-value查询最快的方法就是hash查询,但如果使用动态的节点表示方式

一个节点需要的节点空间20字节,20亿节点占用的存储空间=33GB,需要重点考虑怎么减少单个节点占用的内存。

利用文件多线程并行框架,将数据节点插入hash。这里的hash是需要动态增加的。因此20亿记录需要消耗40GB以上的内存。

数据插入完成后,需要按照hash桶id来进行数据聚合和排序;同时也是把动态索引变成只读静态索引。由于涉及数据集的记录数太多,如果使用磁盘将会是一个比较艰巨的任务,在不考虑调度的情况下,20亿记录,100块磁盘(4KB iops 3000),仅仅在数据重排上面的消耗就会达到6666秒。而现在使用一块ssd就可以轻松完成。

重排后的数据,如果是在线查询,则不用压缩;如果是离线批量查询,则可以使用zstd压缩。这样在线和离线基本在算法层面实现了统一。

bucket数据和node数据全部加载到内存,data数据位于ssd磁盘,如果内存足够也可以将data全部加载到ramfs。

使用二分法查找到所有key2的节点,如果未找到,表示匹配不成功。否则需要进行二次匹配

根据二次hash获得key2,使用二分的方式查找所有key2的节点,由于key2是有序的,获得第一个key2所在数据块的偏移offset和所有key2记录的累加长度length。

利用ssd的随机读写能力直接读取或试用aio的方式读取[data_base+offset,length]数据到内存,逐条记录比较关键字。

cache需要根据数据实际的重复访问率和命中率来确定。但ssd的能力实在比较强,可以暂时不考虑。

随着多模态,序列特征的引入,造成了特征的膨胀,如果在线时还使用redis去查询这种大尺寸的特征,同时把拼接好的特征传递给tf servering做预测,网络将成为第一瓶颈。以前的微服务部署就不适合这种业务场景。

可以充分利用单机强悍的特点,将接入、只读特征数据、多个模型inferene、后处理等都部署在一台物理服务上,其他经常变化的数据部署在Redis上面。这样即可以实现业务逻辑的拆分,又能很好地保证时延的需求。通过这种方法,对应业务的时延降低了80%。

目前从hdfs导出大量数据到idc服务器比较慢clashheroes。 100亿级以上的数据还会涉及到多磁盘并行和单机内存不足的问题clash运行中0bytes,,目前因为没没有需求,还未考虑。

多核,ssd,大内存等服务器的普及,使得原来一些只能分布式处理的数据大大简化,性能有了很大的提升;有些工作项目组申请1,2台ssd服务器就可以完成,无需竞争有限公用资源。同时算法的迭代速度也达到加快,减少等待时间。公司多年来在海量数据处理方面积累的大量经验,可以在数据驱动的时代得到很好的继承和应用。

THE END
喜欢就支持一下吧
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称