Skip to main content

性能报告

本文主要列举torchpipe的基础性能测试实验。triton inference server是我们的主要参照物,然而我们的目的并非与其进行性能竞赛。 框架与框架之间的性能差异有时候很难进行直接的对比, 部分实验中我们采取了合理的非公平对比,用来帮助使用者理解两种框架的特点。

备注

需要 服务性能的关键评价指标 章节作为铺垫。

测试机器情况参见尾注*.

本地函数 vs RPC调用

torchpipe 以线程安全的本地调用为主要使用方式,并绑定pytorch前端接口。这是他的一大特色。当与 triton 这些与RPC强耦合*的框架进行性能对比时并不容易。为此,我们准备采取合理的不公平对比。虽然如此,我们尽可能合理地优化了测试流程,如将所有数据准备都在初始化时提前完成并重复利用。

在实验中我们发现,包含序列化/反序列化的大数据量的RPC调用中,triton和thrift有一定的性能差距。这使得我们很难直接对调度和计算部分进行对比。为此,我们采取对照的方式,首先测定纯粹RPC服务的性能作为对比基数。

信息

当纯RPC服务的吞吐比纯粹计算后端服务的吞吐高一个量级的时候,可以认为RPC对于整个的服务的影响不大;否则,RPC部分可能产生显著的影响。

  • 强耦合*: triton 提供了C语言本地接口,然而并非面向用户的推荐方式。

Hello, World!

我们发送一条HelloWorld讯息并寻求返回相同字符串。

经过测试,我们发现triton对于TYPE_STRING类型的处理并不友好,相同的数据,以TYPE_STRING格式发送以及以TYPE_UINT8格式发送,速度差异较为明显,所以以下实验,我们发送‘HelloWorld’的时候,均采用了triton推荐的UINT8类型进行发送,即将其转换为ASCII码对应的UINT8类型。

空载性能

这部分只作为速度的参照背景使用。 这部分的性能差距在实际业务中(计算密集型后端)影响并不大。

thrift采用了多线程模式, 以python为客户端/服务端语言。

客户端1/计算后端实例1/超时时间0/max_batch1

python后端c++后端
torchpipieQPS: 65227
QPS: 70671
torchpipie+thriftQPS: 10020
QPS: 10444
thriftQPS: 14890
-
triton-cliQPS: 7342
-

相对于rpc框架本身数十万以上的并发要求, torchpipetriton本身的qps并不高。视觉领域的服务是计算密集型的,通常并不能算一种高并发服务,实际单卡QPS一般在100-2000之间;以上实测空载性能上限满足要求。

多客户端加压

这部分只作为速度的参照背景使用。 这部分的性能差距在实际业务中(计算密集型后端)影响并不大。

客户端加压的情况下,差距缓解。

客户端10/计算后端实例1/超时时间0/max_batch1

python后端c++后端
torchpipieQPS: 46655
QPS: 55949
torchpipie-thriftQPS: 13221QPS: 14977
thriftQPS: 17262
-
triton-cliQPS: 15039
-

凑batch过程自适应客户端数目

当流量不足以覆盖max_batch时,系统在等待凑batch的过程中,将以超时为代价。此节测试了这种情况下自适应客户端的情况。

本节使用torchpipie 和 triton 的python后端。

计算后端实例1/超时时间10ms/max_batch4

客户端数目torchpipetriton
1TP50: 0.25TP50: 10.5
2TP50: 10.25TP50: 10.57
4TP50: 0.34TP50: 0.5
10TP50: 0.82TP50: 0.97

实验验证了torchpipie采取的保守的自适应策略,能在一定情况下,减少白白等待凑batch的时间。

max_batch=1时的凑batch效率

测试最大batchsize为1时的系统效率。理论上此时应该忽略掉凑batch过程。

本节使用torchpipie 和 triton 的python后端。

计算后端实例1/超时时间10ms/max_batch1

客户端数目torchpipietriton
1TP50: 0.09TP50: 0.23
10TP50: 0.64TP50: 1.26

小结

python后端(cpu)并发性能

torchpipe原生支持多线程模式,然而由于GIL锁的影响,此种模式对于python计算后端注定有比较大的缺陷;

类型模式备注
GPU后端多线程多进程下需要虚拟化,存在共享显存传递的问题
不涉及GIL锁的CPU后端可多线程-
涉及GIL锁的CPU后端多进程/RPC在python后端中使用multiprocessing/Joblib库,或者桥接第三方RPC框架

以下表格验证了多进程模式处理GIL锁的有效性(后端进行了python的1-100000的累加操作):

客户端10/计算后端实例10/超时时间0/max_batch1

torchpipie(python后端)tritontorchpipie-multiprocess
QPS: 233QPS: 814QPS: 872

纯cpu后端效率对比

本节测试jpg解码性能。输入数据为未解码的原始jpg图片,输出为大小为640x444x3的uint8的tensor. 在本节实验中,我们显式排除RPC框架本身带来的性能差距:客户端发送给我们Hello, world!这一数据,我们在服务端自己准备jpg数据,调用

cv2.imdecode(raw_jpg, cv2.IMREAD_COLOR)

解码,然后返回Hello, world!到客户端。

客户端1/计算后端实例1/超时时间0/max_batch1

torchpipie-thrift(c++后端)torchpipie-thrift(python后端)torchpipie-thrift-multiprocesstriton-cli
QPS: 426QPS: 406QPS: -QPS: 339

客户端10/计算后端实例10/超时时间0/max_batch1

torchpipie-thrift(c++后端)torchpipie-thrift(python后端)torchpipie-thrift-multiprocesstriton-cli
QPS: 1599QPS: 1334QPS: -1440

实验结果与预期相符:各种方式下的效率相近。最终,在十客户端下, torchpipie-thrift(c++后端) > triton-cli > torchpipie-thrift(python后端).

resnet50

本节测试通常情况下,使用tensorrt处理resnet50模型的效率。 客户端输入为224x224x3的图片数据,输出为1x1000。 作为对照,我们补充了纯粹RPC效率的对比:此时服务端预先准备好数据,而不做任何计算。

基础测试

客户端1/计算后端实例1/超时时间0/max_batch1

torchpipie-thrift-rpctriton-rpctorchpipie-thrifttriton-cli备注
QPS: 7519QPS: 1819QPS: 381QPS: 358

客户端10/计算后端实例10/超时时间0/max_batch1

torchpipie-thrift-rpctriton-rpctorchpipie-thrifttriton-cli备注
QPS: 11938QPS: 3355QPS: 720QPS: 722

凑batch

客户端10/计算后端实例2/超时时间5/max_batch4

torchpipie-thrifttriton-cli备注
QPS: 907QPS: 898-

实际使用测试

实际使用中,triton经常需要以RPC调用服务端的方式串联多个节点/模型,而torchpipe的使用方式是本地调用,我们来看看两种方式下的性能对比情况。

基础测试

在如下的测试中,triton和torchpipe均只含有一个resnet18模型,模型均采用FP32的tensorrt加速,输入数据为shape:3x224x224的数据。

客户端1/计算后端实例1/超时时间0/max_batch1

torchpipietriton-rpc备注
QPS: 559QPS: 462

客户端10/计算后端实例1/超时时间0/max_batch1

torchpipietriton-rpc备注
QPS: 1303QPS: 907

与预期一致,torchpipe的本地调用方式在单卡情况下有一定的优势。