FastAPI: результаты производительности различаются между run_in_threadpool() и run_in_executor() с ThreadPoolExecutorPython

Программы на Python
Ответить
Anonymous
 FastAPI: результаты производительности различаются между run_in_threadpool() и run_in_executor() с ThreadPoolExecutor

Сообщение Anonymous »

Вот минимальный воспроизводимый пример моего приложения FastAPI. У меня странное поведение, и я не уверен, что понимаю причину.
Я использую ApacheBench (ab) для отправки нескольких запросов следующим образом:
ab -n 1000 -c 50 -H 'accept: application/json' -H 'x-data-origin: source' 'http://localhost:8001/test/async'

Приложение FastAPI
import time
import asyncio
import enum
from typing import Any

from fastapi import FastAPI, Path, Body
from starlette.concurrency import run_in_threadpool

app = FastAPI()
loop = asyncio.get_running_loop()
def sync_func() -> None:
time.sleep(3)
print("sync func")

async def sync_async_with_fastapi_thread() -> None:
await run_in_threadpool( time.sleep, 3)
print("sync async with fastapi thread")

async def sync_async_func() -> None:
await loop.run_in_executor(None, time.sleep, 3)

async def async_func() -> Any:
await asyncio.sleep(3)
print("async func")

@app.get("/test/sync")
def test_sync() -> None:
sync_func()
print("sync")

@app.get("/test/async")
async def test_async() -> None:
await async_func()
print("async")

@app.get("/test/sync_async")
async def test_sync_async() -> None:
await sync_async_func()
print("sync async")

@app.get("/test/sync_async_fastapi")
async def test_sync_async_with_fastapi_thread() -> None:
await sync_async_with_fastapi_thread()
print("sync async with fastapi thread")

Вот результаты ApacheBench:
асинхронность с (asyncio.sleep):
*Уровень параллелизма: 50
  • Время, затраченное на тесты: 63,528 секунды
  • Выполнено запросов: 1000
    < li>Ошибочные запросы: 0
  • Всего передано: 128 000 байт.
  • Передано HTML: 4 000 байт.
  • Запросов в секунду: 15,74 [ #/сек] (среднее)
  • Время на запрос: 3176,407 [мс] (среднее)
  • Время на запрос : 63,528 [мс] (в среднем по всем одновременным запросам)
    Скорость передачи: получено 1,97 [Кбайт/сек]*
sync (с time.sleep):
Уровень параллелизма: 50
  • *Время, затраченное на тесты: 78,615 секунд
  • Выполненных запросов: 1000
  • Неудачных запросов: 0
  • Всего передано: 128 000 байт
  • Переданный HTML: 4000 байт
  • Запросов в секунду: 12,72 [#/сек] (среднее)
  • Время на запрос: 3930,751 [мс] (среднее)
  • Время на запрос: 78,615 [мс] (среднее по всем одновременным запросам)
    Скорость передачи: 1,59 [Кбайт/сек] ] получено*
sync_async (время сна с run_in_executor) : *Уровень параллелизма: 50
  • Время, затраченное на тесты: 256,201 секунды
  • Выполнено запросов: 1000
    < li>Неудачных запросов: 0.
  • Всего передано: 128 000 байт.
  • Передано HTML: 4 000 байт.
  • Запросы. в секунду: 3,90 [#/сек] (среднее)
  • Время на запрос: 12810,038 [мс] (среднее)
  • Время на запрос: 256,201 [мс] (в среднем по всем одновременным запросам)
    Скорость передачи: получено 0,49 [Кбайт/сек]*
sync_async_fastapi (время ожидания с пулом потоков run_in):
*Уровень параллелизма: 50
  • Затраченное время для тестов: 78,877 секунд.
  • Завершенных запросов: 1000.
  • Неудачных запросов: 0.
  • Всего передано: 128000. байт
  • Передано HTML: 4000 байт
  • Запросов в секунду: 12,68 [#/сек] (среднее)
    < li>Время на запрос: 3943,841 [мс] (среднее)
  • Время на запрос: 78,877 [мс] (среднее по всем одновременным запросам)
    Скорость передачи: получено 1,58 [Кбайт/сек]*
В заключение, я испытываю удивительное несоответствие в результатах, особенно при использовании run_in_executor, где я сталкиваюсь со значительно более высоким средним временем (12 секунд). Я не понимаю такого результата.
--- РЕДАКТИРОВАТЬ ---
После ответа AKX.
Here the code working as expected:
import time
import asyncio
from anyio import to_thread

to_thread.current_default_thread_limiter().total_tokens = 200
loop = asyncio.get_running_loop()
executor = ThreadPoolExecutor(max_workers=100)
def sync_func() -> None:
time.sleep(3)
print("sync func")

async def sync_async_with_fastapi_thread() -> None:
await run_in_threadpool( time.sleep, 3)
print("sync async with fastapi thread")

async def sync_async_func() -> None:
await loop.run_in_executor(executor, time.sleep, 3)

async def async_func() -> Any:
await asyncio.sleep(3)
print("async func")

@app.get("/test/sync")
def test_sync() -> None:
sync_func()
print("sync")

@app.get("/test/async")
async def test_async() -> None:
await async_func()
print("async")

@app.get("/test/sync_async")
async def test_sync_async() -> None:
await sync_async_func()
print("sync async")

@app.get("/test/sync_async_fastapi")
async def test_sync_async_with_fastapi_thread() -> None:
await sync_async_with_fastapi_thread()
print("sync async with fastapi thread")


Подробнее здесь: https://stackoverflow.com/questions/779 ... n-in-execu
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»