第二部分,我们进行了多卡优化。由于我们采购的 CPU 是 AMD 的,存在 NUMA 的问题:在线阶段跨 NUMA 访问内存的时间远高于 CPU 直接访问本地内存的时间。因此我们在使用过程中对Pod 进行了 NUMA 绑定,绑定后的 Pod 不需要跨 NUMA 内存申请与节点调用,提高了 CPU 与 GPU 之间的数据传输速度,减少了 CPU 阶段的内存消耗。CPU 阶段速度变快,可以给后面 GPU Inference 留更多的时间。(3)编译优化
计算优化的一个较为明智的思路是找到冗余的计算量进行优化。推搜类的 CTR 模型存在了大量的冗余计算:推搜场景较为突出的特点是 1 个 user + N 个 item 的预估,而 Tensor 结构在原始计算 input 阶段需要展平,也就是 user 侧计算从数据侧会拆分成 N 份,而这部分计算是冗余的。第二个冗余是在使用原始 TensorFlow API 搭建整个 graph 时,一次请求数据会多次进GPU 计算处理,会有部分计算在 CPU 结束后传给 GPU,而这部分是不够高效的。因此我们在此进行了算子合并、优化推导,利用 CUDA 的 API 统一实现,让一次请求只进一次 GPU,减少多次进 GPU 的冗余计算功能。此外,还可以计算前置,减少冗余计算。比如在产出模型时,需要对变量算子进行冻结,把变量算子转换成常量算子,具体举例来说,在图冻结过程中,有+1+2 计算,可以静态地直接转换成 +3。图冻结化在产出中让整个计算使用率下降了 12%,非常有效。合并计算部分我们需要尽量做到用 GPU 的 CUDA 实现,减少外部数据计算完再进 GPU 的拷贝问题,会带来较高的性能提升。(5)换更好的硬件
计算优化中一个非常直接的优化就是换更好的硬件。在线推理阶段没必要用到 A100 这种特别贵的卡,可以用 A10 替换 T4,提升了 1.5 倍的性能,但只花了 1.2 倍的钱。未来我们还会考虑在线使用 A30 等机型进行推理。同时,随着硬件厂商的版本迭代,我们也会直接去换更好的硬件,取得线上的成本收益。但换了硬件后,前置放在 CPU 上的计算部分会更加鸡肋,总会存在一些 Vlookup 特征,合并交叉哈希计算等不适合在 GPU 上处理。这时候则需要推进到第三步,也就是我们会浪费一些时延,把前面 CPU 计算部分拆分出去。Inference 架构会变为请求先经过 CPU 计算,后进行 GPU 在线推理。后面随着硬件 GPU 不断升级,CPU 实在跟不上的情况下,则会继续拆分。(6)DL 栈自动编译优化