当前位置: 首页 > 后端技术 > Python

GPU推理服务性能优化之路-得物科技

时间:2023-03-26 00:32:24 Python

1背景随着CV算法在业务场景中的应用越来越多,给我们带来了新的挑战。我们需要提高Python推理服务的性能,以降低生产环境成本。为此,我们将深入探讨PythonGPU推理服务的工作原理和推理模型优化的方法。最终采用了两个关键技术:1.Python的GPU和CPU进程分离,2.使用TensorRT对模型进行加速,使得大部分在线模型服务的QPS提升了5-10倍左右,节省了大量资源在线GPU推理服务的成本。针对以上两项关键技术,我们也开发了相关的框架和工具进行沉淀。包括用于自动隔离CPU和GPU进程的基于Python的推理服务框架,以及用于将推理模型转换为TensorRT优化的调试工具。此外,针对不同的推理服务性能瓶颈,我们还整理了各种实用的优化技术,如CPU与GPU分离、TensorRT半精度优化、同模型混合部署、GPU数据传输与推理并行化等,下面从理论、框架与工具、实用的优化技巧三个方面介绍推理服务性能优化的方法。2理论2.1CUDA体系结构CUDA是NVIDIA发明的并行计算平台和编程模型。它通过利用图形处理单元(GPU)的处理能力显着提高计算性能。CUDA架构引入了主机(host,cpu)和设备(device,gpu)的概念。CUDA的Kernel函数既可以运行在主机端,也可以运行在设备端。同时可以在主机端和设备端之间进行数据拷贝。CUDAKernelfunction:是数据并行处理函数(kernelfunction)。在GPU上执行时,一个Kernel对应一个Grid,根据GPU逻辑架构分布到多个线程中并行执行。CUDAStream流:Cuda流是指一堆异步的cuda操作,按照调用宿主代码的顺序在设备上执行。典型的CUDA代码执行流程:将数据从主机复制到设备。b.在设备上执行内核。C。将Device段的结果复制到Host端。以上过程也是GPU上模型推理的过程。在执行的过程中,还需要绑定CUDAStream,以流的形式执行。2.2传统Python推理服务的瓶颈2.2.1传统Python推理服务架构由于Python在神经网络训练和推理领域提供了丰富的库支持,以及Python语言本身的便利性,推理服务大多采用Python实现。CV算法的推理引擎大多直接使用Pythonflask框架或Kserve框架实现。该框架的一般调用流程如下:上述架构是传统推理服务的通用架构。这种架构的好处是代码更容易理解。但是在性能上有很大的劣势,可以承载的QPS比较低。我们使用了几个CV模型进行压力测试,限制QPS一般不超过4。2.2.2瓶颈分析因为上述架构的CPU逻辑(图片的前处理和后处理)和GPU逻辑(模型推理)处于同一个线程中,会存在以下性能瓶颈:如果是单线程模式,CPU逻辑和GPU逻辑相互等待,GPUKernel函数调度不足,导致GPU使用率低.无法充分提高QPS。这种情况只能开更多的进程来提高QPS,但是更多的进程会带来更多的显存开销。如果开启多线程模式,经过实测,这种方式并不能带来QPS的提升。主要是因为Python的GIL锁,由于Python的GIL锁的存在,Python的多线程实际上是伪多线程,并不是真正的并发执行,而是多个线程通过竞争GIL锁来执行。在这种情况下,GPU内核启动线程无法被完全调度。在Python推理服务中,开启多线程会导致GPUKernel启动线程频繁被CPU线程打断。由于GPUkernellanch调度不足,该方法无法充分利用GPU的使用率。2.2.3解决方案针对以上问题,我们的解决方案是将CPU逻辑和GPU逻辑分离在两个不同的进程中。CPU进程主要负责图像的前处理和后处理,GPU逻辑主要负责执行cuda核函数,即模型推理。另外,因为我们有大量的推理服务在线运行,所以我们开发了一个基于Python的统一框架,将CPU和GPU分开。对于原有的Flask或Kserve服务,我们的服务稍作修改即可使用。具体可以参考下面CPU和GPU分离的统一推理框架介绍。对于在线推理服务,我们的框架用于分离CPU和GPU进程。压测得到的数据如下,可见QPS提升了7倍左右。2.3TensorRT模型加速原理TensorRT是NVIDIA公司推出的高性能深度学习模型推理软件开发工具包。它可以将优化的深度学习模型构建到推理引擎中,并将其部署到实际生产环境中。TensorRT提供基于硬件的推理引擎性能优化。下图展示了业界最常用的TensorRT优化流程,也是目前模型优化的最佳实践,即先将pytorch或tensorflow等模型转成onnx格式,再将onnx格式转成TensorRT用于优化。其中,TensorRT所做的工作主要分为两个时期,一个是网络建设时期,一个是模型运行时期。A。网络建设期i.模型分析与建立,加载onnx网络模型。二.计算图优化,包括水平算子融合,或垂直算子融合等。iii.节点淘汰,去掉无用的节点。四.多精度支持,支持FP32/FP16/int8等精度。v.基于特定硬件的相关优化。b.模型运行时i。序列化并加载RensorRT模型文件。二.提供运行时环境,包括对象生命周期管理、内存和显存管理等。下面是我们基于VisualTransformer模型进行TensorRT优化前后的性能评估报告:3框架和工具本章主要介绍我们自己的框架和工具。框架是CPU和GPU分离的Python统一推理框架,工具是Onnx转TensorRT的半自动调试工具。我们正在推广大量使用相关框架和工具的在线推理服务。其中,CPU和GPU分离的Python统一推理框架解决了普通Python推理服务无法自动隔离CPU和GPU的问题。用户只需要继承和实现框架提供的预处理、推理和后处理接口,底层逻辑就可以自动实现CPU和GPU的进程级隔离。其中TensorRT半自动调试工具主要定位解决模型转TensorRT过程中遇到的各种精度损失问题。底层基于TensorRT的相关接口和工具进行封装开发。简化TensorRT的优化参数。3.1CPU与GPU分离的统一推理框架新架构设计方案如下:方案设计思路是将GPU逻辑和CPU逻辑分离成两个进程,其中CPU进程主要负责负责CPU相关的业务逻辑,GPUprocessmaster负责GPU相关的推理逻辑。同时启动一个Proxy进程进行路由转发。(1)Proxy进程Proxy进程是系统的门面,对外提供调用接口,主要负责路由分发和健康检查。Proxy进程收到请求后,轮询CPU进程,将请求分发给CPU进程。(2)CPU进程CPU进程主要负责推理服务中与CPU相关的逻辑,包括前处理和后处理。预处理一般是图片解码和图片转换。后处理一般是推理结果判断等逻辑。预处理完成后,CPU进程会调用GPU进程进行推理,然后继续进行后处理相关逻辑。CPU进程和GPU进程通过共享内存或网络进行通信。共享内存可以减少图片的网络传输。(3)GPU进程GPU进程主要负责运行GPU推理相关的逻辑。它启动时会加载很多模型到显存中,然后在收到CPU进程的推理请求后,会直接触发kernellanuch调用模型进行推理。本方案为算法同学提供了Model类接口。算法同学不需要关心后面的调用逻辑。他们只需要填写预处理和后处理业务逻辑。模型服务可以快速启动,这些进程会自动启动。该方案将CPU逻辑(图片解码、图片后处理等)和GPU逻辑(模型推理)分离为两个不同的过程。可以解决PythonGIL锁导致的GPUKernel启动调度问题。3.2TensorRT调试工具TensorRT虽然没有完全开源,但是官方已经提供了一些接口和工具。基于这些接口和工具,我们可以分析和干预模型优化过程。基于TensorRT官方提供的接口和工具,我们自己开发了一套工具。用户可以使用我们的工具将模型转换为TensorRT格式。如果在模型转换过程中出现精度丢失等问题,用户也可以使用该工具定位并解决问题。自研工具主要分两个阶段为用户提供帮助,一个阶段是问题定位,一个阶段是模型转换。具体描述如下:3.2.1问题定位问题定位阶段主要解决模型转为TensorRT并开启FP16模式时精度损失的问题。对于一般的分类模型,如果精度要求不高,尽量开启FP16。在FP16模式下,NVIDIA为FP16配备了专用的TensorCores,可以进行矩阵运算,吞吐量相比FP32提高了一倍以上。比如转换为TensorRT时,开启FP16会出现精度损失问题。自研工具在问题定位阶段的大致工作流程如下:主要工作流程为:(1)设置模型转换精度要求后,将所有算子标记为输出,然后比较所有算子的输出精度。(2)找到最早不满足精度要求的算子,通过以下方式进行干预。将此运算符标记为FP32。将其父运算符标记为FP32。改变算子的优化策略(详见TensorRT的tactic),循环上述两步,最终找到满足目标精度要求的模型参数。这些参数,比如需要额外启用FP32的那些operators等。然后会将相关的参数输出到配置文件中,如下:3.2.2模型转换在模型转换阶段,将上述问题得到的参数直接使用locationstage,调用TensorRT相关接口和工具进行转换。另外,在模型转换阶段,我们也做了一些封装,解决了原来TensorRT的参数和API过于复杂的问题,提供了更简洁的接口。例如,该工具可以自动解析ONNX,判断模型的输入输出形状。然后用户提供相关的形状信息。4实践中的优化技巧在实际应用中,我们希望用户能够为推理模型启用CPU和GPU分离,同时启用TensorRT优化。这样往往可以得到QPS两次优化的叠加效果。比如我们离线优化某个分类模型,使用CPU和GPU分离,TensorRT优化,并启用FP16半精度,最终得到10倍的QPS提升。以下是模型优化过程中的一些实用技巧,整理分享给大家。(1)分类模型,CPU和GPU分离,优化TensorRT,开启FP16,QPS提升10倍。基于Resnet的在线分类模型可以接受0.001的精度损失(误差定义:median,atol,rtol)范围的误差。因此,我们对推理服务进行了三项性能优化:使用我们统一的GPU和CPU分离框架进行改造。b.模型传输到ONNX后,再传输到TensorRT。C。开启FP16模式,使用自研工具定位中间有精度损失的算子,将这些算子标记为FP32。经过上面的优化,QPS最终提升了10倍(与原来的Pytorch直接推理相比),成本大大降低。(2)检测模型,CPU和GPU分离,TensorRT模型优化,QPS提升约4-5倍。一个基于Yolo的在线检测模型,精度要求比较高,所以没办法开启FP16。我们直接在FP32模式下优化TensorRT,并使用统一的框架将GPU和CPU分离,最终获得了QPS4-5倍的提升。(3)重复部署同一个模型,充分利用GPU算力资源在实际场景中,往往GPU算力充足,但GPU内存不够用。经过TensorRT优化后,模型运行所需的显存大小普遍降低到原来的1/3到1/2。为了充分利用GPU的算力,框架进一步优化,支持一个容器内GPU进程的多副本。这种架构既保证了CPU能够向GPU提供足够的请求,又保证了GPU算力得到充分利用。优化后的架构如下图所示:5总结以上两种推理模型的加速技术,即CPU和GPU进程的隔离,以及TensorRT模型的加速。我们在线优化了大量的GPU推理服务,也节省了大量的GPU服务器成本。CPU和GPU进程隔离主要是为了Python推理服务的优化,因为在C++推理服务中,没有PythonGIL锁,不存在PythonKernel启动线程的调度问题。目前业界开源的Python推理服务框架还没有提供类似的优化功能,所以未来我们会考虑把Python统一推理服务框架开源,希望能为社区做点贡献。另外,对于TensorRT的模型优化,我们参考了大量的NIVIDIA官网文档,封装在上层,以后会进一步研究。