7-3_Python_Pipeline_Optimize_CN.md 9.0 KB
Newer Older
1 2
# Python Pipeline 优化指南

T
TeslaZhao 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15
- [优化响应时长](#1)
  - [1.1 分析响应时长](#1.1)
    - [Pipeline Trace Tool](#1.1.1)
    - [Pipeline Profile Tool](#1.1.2)
  - [1.2 优化思路](#1.2)
- [优化服务吞吐](#2)
  - [2.1 分析吞吐瓶颈](#2.1)
  - [2.2 优化思路](#2.2)
    - [增加 Op 并发](#2.2.1)
    - [动态批量](#2.2.2)
    - [CPU 与 GPU 处理分离](#2.2.3)


T
TeslaZhao 已提交
16 17 18
通常,服务的性能优化是基于耗时分析,首先要掌握服务运行的各阶段耗时信息,从中找到耗时最长的性能瓶颈再做针对性优化。对于模型推理服务化不仅要关注耗时,由于 GPU 芯片昂贵,更要关注服务吞吐,从而提升 GPU 利用率实现降本增效。因此,模型推理服务化可总结为:
- 优化响应时长
- 优化服务吞吐
19

T
TeslaZhao 已提交
20
经过分析和调优后,各个阶段实现整体服务的性能最优。
21

T
TeslaZhao 已提交
22 23
<a name="1"></a>

T
TeslaZhao 已提交
24
## 优化响应时长
25

T
TeslaZhao 已提交
26
首先,优化响应时长的主要思路首先要掌握各阶段耗时,并分析出性能瓶颈或者耗时占比较高的阶段,再针对性能瓶颈做专项优化。
27

T
TeslaZhao 已提交
28 29 30
Paddle Serving 提供2种耗时分析工具,`Pipeline Trace Tool``Pipeline Profile Tool`。2个工具的特点如下:
- Pipeline Trace Tool : 统计服务端所有进程各个阶段的平均耗时,包括每个 `Op``Channel`,用于定量分析。
- Pipeline Profile Tool : 是可视化 Trace View 工具,生成多进程并发效果图,用定性和定量分析执行和并发效果。
31

T
TeslaZhao 已提交
32 33 34 35 36 37 38
<a name="1.1"></a>

**一.耗时分析**

<a name="1.1.1"></a>

1.Pipeline Trace Tool
T
TeslaZhao 已提交
39 40 41 42 43

`Pipeline Trace Tool` 统计每个 `Op``Channel` 中各阶段的处理耗时,

开启方法在配置文件 `config.yml``dag` 区段内添加 `tracer` 字段,框架会每隔 `interval_s` 时间生成 Trace 信息。
```
44
dag:
T
TeslaZhao 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    #op资源类型, True, 为线程模型;False,为进程模型
    is_thread_op: True

    #tracer, 跟踪框架吞吐,每个OP和channel的工作情况。无tracer时不生成数据
    tracer:
        #每次trace的时间间隔,单位秒/s
        interval_s: 10
```

生成的 Trace 信息保存在 `./PipelineServingLogs/pipeline.tracer` 日志中。如下图所示
```
==================== TRACER ======================
 Op(uci):
         in[8473.507333333333 ms]:          # 等待前置 Channel 中数据放入 Op 的耗时,如长时间无请求,此值会变大
         prep[0.6753333333333333 ms]         # 推理前处理 preprocess 阶段耗时 
         midp[26.476333333333333 ms]         # 推理 process 阶段耗时
         postp[1.8616666666666666 ms]        # 推理后处理 postprocess 阶段耗时
         out[1.3236666666666668 ms]          # 后处理结果放入后置 channel 耗时
         idle[0.9965882097324374]            # 框架自循环耗时,间隔 1 ms,如此值很大说明系统负载高,调度变慢
 DAGExecutor:
         Query count[30]                     # interval_s 间隔时间内请求数量 
         QPS[27.35 q/s]                      # interval_s 间隔时间内服务 QPS 
         Succ[1.0]                           # interval_s 间隔时间内请求成功率 
         Error req[]                         # 异常请求信息
         Latency:                
                 ave[36.55233333333334 ms]   # 平均延时
                 .50[8.702 ms]               # 50分位延时
                 .60[8.702 ms]               # 60分位延时
                 .70[92.346 ms]              # 70分位延时
                 .80[92.346 ms]              # 70分位延时
                 .90[92.346 ms]              # 90分位延时
                 .95[92.346 ms]              # 95分位延时
                 .99[92.346 ms]              # 99分位延时
 Channel (server worker num[1]):
         chl0(In: ['@DAGExecutor'], Out: ['uci']) size[0/0]  # 框架 RequestOp 与 uci Op 之间 Channel 中堆积请求数。此值较大,说明下游 uci Op 消费能力不足。
         chl1(In: ['uci'], Out: ['@DAGExecutor']) size[0/0]  # uci Op 与 框架 ResponseOp 之间 Channel 中堆积的请求数。此值较大,说明下游 ReponseOp 消费能力不足。
 ==================== TRACER ======================
82
```
T
TeslaZhao 已提交
83
<a name="1.1.2"></a>
84

T
TeslaZhao 已提交
85
2.Pipeline Profile Tool
86

T
TeslaZhao 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99
```
dag:
    #op资源类型, True, 为线程模型;False,为进程模型
    is_thread_op: True
    
    #使用性能分析, 默认为 False,imeline性能数据,对性能有一定影响
    use_profile: True,
```

开启后,Server 端在预测的过程中会将对应的日志信息打印到`标准输出`,为了更直观地展现各阶段的耗时,因此服务启动要使用如下命令:
```
python3.7 web_service.py > profile.txt 2>&1
```
100

T
TeslaZhao 已提交
101 102
服务接收请求后,输出 Profile 信息到 `profile.txt` 文件中。再粘贴如下代码到 `trace.py`, 使用框架提供 Analyst 模块对日志文件做进一步的分析处理。
```
103 104 105 106 107 108 109 110 111 112 113
from paddle_serving_server.pipeline import Analyst
import json
import sys

if __name__ == "__main__":
    log_filename = "profile.txt"
    trace_filename = "trace"
    analyst = Analyst(log_filename)
    analyst.save_trace(trace_filename)
```

T
TeslaZhao 已提交
114 115 116 117 118 119 120 121 122 123 124
运行命令,脚本将日志中的时间打点信息转换成 json 格式保存到 `trace` 文件。
```
python3.7 trace.py
```

`trace` 文件可以通过 `chrome` 浏览器的 `tracing` 功能进行可视化。
```
打开 chrome 浏览器,在地址栏输入 chrome://tracing/ ,跳转至 tracing 页面,点击 load 按钮,打开保存的 trace 文件,即可将预测服务的各阶段时间信息可视化。
```

通过图示中并发请求的处理流程可观测到推理阶段的流水线状态,以及多个请求在推理阶段的`间隔`信息,进行优化。
125

T
TeslaZhao 已提交
126
<a name="1.2"></a>
127

T
TeslaZhao 已提交
128
**二.降低响应时长优化思路**
129

T
TeslaZhao 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
根据 `Pipeline Trace Tool` 输出结果在不同阶段耗时长的问题,常见场景的优化方法如下:
- Op 推理阶段(midp) 耗时长:
  - 增加 Op 并发度
  - 开启 auto-batching (前提是多个请求的 shape 一致)
  - 若批量数据中某条数据的 shape 很大,padding 很大导致推理很慢,可参考 OCR 示例中 mini-batch 方法。
  - 开启 TensorRT/MKL-DNN 优化
  - 开启低精度推理
- Op 前处理阶段(prep) 或 后处理阶段耗时长:
  - 增加 OP 并发度
  - 优化前后处理逻辑
- in/out 耗时长(channel 堆积>5)
  - 检查 channel 传递的数据大小,可能为传输的数据大导致延迟大。
  - 优化传入数据,不传递数据或压缩后再传入
  - 增加 Op 并发度
  - 减少上游 Op 并发度
145

T
TeslaZhao 已提交
146 147 148 149 150 151
根据 `Pipeline Profile Tool` 输出结果优化流水行并发的效果
- 增加 Op 并发度,或调整不同 Op 的并发度
- 开启 auto-batching

此外,还有一些优化思路,如将 CPU 处理较慢的过程转换到 GPU 上处理等,客户端与服务端传输较大数据时,可使用共享内存方式传递内存或显存地址等。

T
TeslaZhao 已提交
152 153
<a name="2"></a>

T
TeslaZhao 已提交
154 155
## 优化服务吞吐

T
TeslaZhao 已提交
156 157 158 159
<a name="2.1"></a>

**一.分析吞吐瓶颈**

T
TeslaZhao 已提交
160
服务的吞吐量受到多种多因素条件制约,如 Op 处理时长、传输数据耗时、并发数和 DAG 图结构等,可以将这些因素进一步拆解,当传输数据不是极端庞大的时候,最重要因素是流水线中`最慢 Op 的处理时长和并发数`
161
```
T
TeslaZhao 已提交
162
Op 处理时长:
163 164 165
op_cost = process(pre + mid + post) 

服务吞吐量:
T
TeslaZhao 已提交
166
service_throughput = 1 / 最慢 op_cost * 并发数
167 168 169 170 171 172 173

服务平响:
service_avg_cost = ∑op_concurrency 【关键路径】

批量预测平均耗时:
avg_batch_cost = (N * pre + mid + post) / N 
```
T
TeslaZhao 已提交
174 175 176
<a name="2.2"></a>

**二.优化思路**
177

T
TeslaZhao 已提交
178
优化吞吐的主要方法是 `增大 Op 并发数``自动批量``CPU 与 GPU 处理分离`
179

T
TeslaZhao 已提交
180 181 182
<a name="2.2.1"></a>

1.增加 Op 并发**
T
TeslaZhao 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195

调整 Op 的并发数量通过设置 `is_thread_op: False` 进程类型 Op 和 `uci` Op 的 `concurrency` 字段
```
dag:
    #op资源类型, True, 为线程模型;False,为进程模型
    is_thread_op: False
op:
    uci:
        #并发数,is_thread_op=True时,为线程并发;否则为进程并发
        concurrency: 10
```
Op 的进程数量不是越大越好,受到机器 CPU 核数、内存和显存大小的限制,推荐设置 Op 的并发数不超过系统 CPU 核数。

T
TeslaZhao 已提交
196 197 198 199 200
<a name="2.2.2"></a>

2.动态批量

动态批量是增加吞吐的有一种方法,开启方式可参考[Python Pipeline 核心功能](./7-2_Python_Pipeline_Senior_CN.md#批量推理)
T
TeslaZhao 已提交
201

T
TeslaZhao 已提交
202
<a name="2.2.3"></a>
T
TeslaZhao 已提交
203

T
TeslaZhao 已提交
204
3.CPU 与 GPU 处理分离
T
TeslaZhao 已提交
205 206 207 208 209 210 211 212 213 214

`CV` 模型中,对图片或视频的前后处理成为主要瓶颈时,可考虑此方案,即将前后处理过程独立成一个 Op 并独立设置并发度。

将 CPU 前后处理和 GPU 推理过程比例调整到服务最佳配比。以 OCR 为例,原有流水线设计为 `RequestOp -> DetOp -> RecOp -> ResponseOp`

根据耗时分析,`DetOp``RecOp` 的前处理耗时很长,因此,将2个模型前处理分离成独立 Op,最新的流水线设计为:

`RequestOp -> PreDetOp -> DetOp -> PreRecOp -> RecOp -> ResponseOp`,并调大 `PreDetOp``PreRecOp`的并发度,从而获得 20% 的性能提升。

由于增加了2次数据传递,单条请求的处理延时会增加。