PyTorch模型 + HTTP API = 执行速度非常慢

我有以下内容:

  • 一个使用PyTorch的机器学习模型,能够将数据向量化并在约3.5毫秒内做出预测(中位数 ≈ 平均数)
  • 一个使用FastAPI + uvicorn的HTTP API,能够在约2毫秒内处理简单请求

但当我将它们结合使用时,中位数响应时间几乎变成了200毫秒

导致这种性能下降的原因可能是什么?


请注意以下几点:

  • 我还尝试了单独使用aiohttp,以及aiohttp + gunicorn和Flask开发服务器进行服务,结果相同
  • 我尝试每秒发送2次、20次和100次请求,结果相同
  • 我意识到并行请求可能会导致延迟降低,但不会降低30倍!
  • CPU负载仅为约7%

这是我测量模型性能的方式(我单独测量了中位数时间,它几乎与平均时间相同):

def predict_all(predictor, data):    for i in range(len(data)):        predictor(data[i])data = load_random_data()predictor = load_predictor()%timeit predict_all(predictor, data)# 手动将总时间除以数据中的记录数

这是FastAPI版本:

from fastapi import FastAPIfrom starlette.requests import Requestfrom my_code import load_predictorapp = FastAPI()app.predictor = load_predictor()@app.post("/")async def root(request: Request):    predictor = request.app.predictor    data = await request.json()    return predictor(data)

HTTP性能测试:

wrk2 -t2 -c50 -d30s -R100 --latency -s post.lua http://localhost:8000/

编辑。

这是我尝试过的稍微修改的版本,带有和不带async:

@app.post("/")# async def root(request: Request, user_dict: dict):def root(request: Request, user_dict: dict):    predictor = request.app.predictor    start_time = time.time()    y = predictor(user_dict)    finish_time = time.time()    logging.info(f"user {user_dict['user_id']}: "                 "prediction made in {:.2f}ms".format((finish_time - start_time) * 1000))    return y

所以我只是添加了预测时间的日志记录。

异步版本的日志:

2021-02-03 11:14:31,822: user 12345678-1234-1234-1234-123456789123: prediction made in 2.87msINFO:     127.0.0.1:49284 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,329: user 12345678-1234-1234-1234-123456789123: prediction made in 3.93msINFO:     127.0.0.1:49286 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,345: user 12345678-1234-1234-1234-123456789123: prediction made in 15.06msINFO:     127.0.0.1:49287 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,351: user 12345678-1234-1234-1234-123456789123: prediction made in 4.78msINFO:     127.0.0.1:49288 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,358: user 12345678-1234-1234-1234-123456789123: prediction made in 6.85msINFO:     127.0.0.1:49289 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,363: user 12345678-1234-1234-1234-123456789123: prediction made in 3.71msINFO:     127.0.0.1:49290 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,369: user 12345678-1234-1234-1234-123456789123: prediction made in 5.49msINFO:     127.0.0.1:49291 - "POST / HTTP/1.1" 200 OK2021-02-03 11:14:56,374: user 12345678-1234-1234-1234-123456789123: prediction made in 5.00ms

所以预测速度很快,平均不到10毫秒,但整个请求需要200毫秒。

同步版本的日志:

2021-02-03 11:17:58,332: user 12345678-1234-1234-1234-123456789123: prediction made in 65.49ms2021-02-03 11:17:58,334: user 12345678-1234-1234-1234-123456789123: prediction made in 23.05msINFO:     127.0.0.1:49481 - "POST / HTTP/1.1" 200 OKINFO:     127.0.0.1:49482 - "POST / HTTP/1.1" 200 OK2021-02-03 11:17:58,338: user 12345678-1234-1234-1234-123456789123: prediction made in 72.39ms2021-02-03 11:17:58,341: user 12345678-1234-1234-1234-123456789123: prediction made in 78.66ms2021-02-03 11:17:58,341: user 12345678-1234-1234-1234-123456789123: prediction made in 85.74ms

现在预测需要很长时间!出于某种原因,相同的调用,但在同步上下文中执行时,开始需要大约30倍的时间。但整个请求所需的时间大致相同 – 160-200毫秒。


回答:

对于进行高强度计算且与其他端点相比可能需要更长时间的端点,请使用非协程处理程序。

当您使用def而不是async def时,FastAPI默认会使用Starlette的run_in_threadpool,其底层也使用loop.run_in_executor

run_in_executor将在默认循环执行器中执行函数,它会在一个单独的线程中执行函数,如果您在进行高CPU密集型工作,您可能还想检查像ProcessPoolExecutorThreadPoolExecutor这样的选项。

这个简单的数学计算在处理协程时非常有帮助。

function   if function_takes ≥ 500ms       use `def`   else       use `async def`

使您的函数成为非协程应该会很有帮助。

@app.post("/")def root(request: Request):    predictor = request.app.predictor    data = await request.json()    return predictor(data)

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注