Это было одновременно опубликовано на моем блог
На выходных из 2020-06-06/07, пост Cal Patterson «Async Python не быстрее» Выскочил на моем радаре на нескольких агрегаторах связи. Я согласен – Async Python не «быстрее», но у меня есть некоторые мысли о себе на эту тему.
Начнем у меня есть несколько предостережений: а) Я не разработчик эксперта б) Я не эксперт на Async C) Я связан с проектом Sanic, веб-каркас Python Async
С учетом того, что я хотел бы немного поговорить о ценности неблокирующих IO очень простой способ.
Рассмотрим это (и да, это тоже немного нереально): вы голодны. Вы решаете, что хотите есть пять гамбургеров, чтобы наделиться своим голодом. Чтобы получить эти гамбургеры, вы идете на ближайший ресторан быстрого обслуживания (Mcsynchronous’s или Micky Sync’s) и закажите их.
Этот ресторан имеет один повар, и этот повар имеет только способность сделать один гамбургер за один раз. Это не отличная настроек, но это делает работу. Требуется некоторое время, чтобы приготовить каждую гамбургер и собирать бургер, но для того, чтобы облегчить вас, ресторан даст вам каждый гамбургер, как только он будет готов. Предполагая, что каждый бургер занимает две минуты, чтобы сделать и собрать, через десять минут у вас будет все ваши гамбургеры.
Это похоже на следующее:
(1) order (2) start burger (3) prep burger (two minutes) (4) deliver burger (5) repeat steps 2 through 4 four times
Общее время: десять минут.
Вниз по улице – еще один сустав (этот называется async & await Burger), который также имеет один повар, но этот повар имеет возможность приготовить несколько гамбургеров одновременно. Для этого повара требуется две минуты для подготовки каждого гамбургера, но поскольку повар имеет преимущество в том, что он имеет возможность совместно подготовить несколько гамбургеров, они могут быть доставлены быстрее. Это сделано, потому что повар не должен сосредоточиться на каждом бургере один за раз, но вместо этого может начать бургер, а затем начать следующий бургер, возвращаясь к каждому бургеру, как он закончен.
Это похоже на следующее:
(1) order (2) start burgers 1-5 (3) prep burgers 1-5 (two minutes each, simultaneously) (4) deliver burgers
Общее время: две минуты.
Наконец, давайте добавим ограничение на наш сценарий бургера: есть только способность подготовить один гамбургер за раз. В этом сценарии, несмотря на ваща, способный справиться с несколькими гаммами, это невозможно, и поэтому потребуется повар десять минут, как и в Micky Sync.
Представьте, однако, что вы хотите только один бургер. Оба «A & A и Micky Sync» занимают столько же времени, чтобы подготовиться только один бургер, поэтому вообще нет преимущества скорости.
Все это то же самое для синхронных и асинхронных веб-каркасов и их «скорость» в целом. Возможность одного процесса для обработки нескольких одновременных запросов является преимуществом асинхронных операций. Если вы размещаете синхронный или блокирующий запрос (нашу способность подготовить только один бургер за один раз, помнить) преимущество удалено.
Я выбрал два высокоценаруемых рамок Python, чтобы продемонстрировать это: Сокол служил Gunicorn и Starlette служил Увикорн. Код как на паритет, так как я могу сделать их для синхронного и асинхронного максимально:
Сокол:
import falcon import time class DefaultResource: def on_get(self, req, resp): """Handles GET requests""" time.sleep(5) resp.media = "200 OK" falconapi = falcon.API() falconapi.add_route('/', DefaultResource())
служить:
Приложение Gunicorn: Фальконапи
Starlette:
from starlette.applications import Starlette from starlette.responses import PlainTextResponse from starlette.routing import Route import asyncio async def default(request): await asyncio.sleep(5) return PlainTextResponse("200 OK") starletteapi = Starlette(debug=True, routes=[ Route('/', default), ])
служить:
Приложение Uvicorn: StarlettePi.
Чтобы проиллюстрировать это, я использовал простой (и старый) инструмент – AB, что делает 5 запросов одновременно следующим образом:
AB -N 5 -C 5 http://localhost: 8000//
Результаты для Сокола:
This is ApacheBench, Version 2.3 <$Revision: 1874286 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient).....done Server Software: gunicorn/20.0.4 Server Hostname: localhost Server Port: 8000 Document Path: / Document Length: 8 bytes Concurrency Level: 5 Time taken for tests: 25.031 seconds Complete requests: 5 Failed requests: 0 Total transferred: 795 bytes HTML transferred: 40 bytes Requests per second: 0.20 [#/sec] (mean) Time per request: 25031.488 [ms] (mean) Time per request: 5006.298 [ms] (mean, across all concurrent requests) Transfer rate: 0.03 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.3 1 1 Processing: 5006 11014 6527.0 12515 20025 Waiting: 5005 11013 6527.2 12515 20024 Total: 5007 11014 6526.9 12516 20025 ERROR: The median and mean for the initial connection time are more than twice the standard deviation apart. These results are NOT reliable. Percentage of the requests served within a certain time (ms) 50% 10013 66% 15018 75% 15018 80% 20025 90% 20025 95% 20025 98% 20025 99% 20025 100% 20025 (longest request)
Результаты для Starlette:
This is ApacheBench, Version 2.3 <$Revision: 1874286 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient).....done Server Software: uvicorn Server Hostname: localhost Server Port: 8000 Document Path: / Document Length: 6 bytes Concurrency Level: 5 Time taken for tests: 10.007 seconds Complete requests: 5 Failed requests: 0 Total transferred: 695 bytes HTML transferred: 30 bytes Requests per second: 0.50 [#/sec] (mean) Time per request: 10007.417 [ms] (mean) Time per request: 2001.483 [ms] (mean, across all concurrent requests) Transfer rate: 0.07 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.5 1 1 Processing: 5003 5003 0.2 5003 5004 Waiting: 5002 5003 0.5 5003 5003 Total: 5003 5004 0.6 5004 5005 Percentage of the requests served within a certain time (ms) 50% 5004 66% 5005 75% 5005 80% 5005 90% 5005 95% 5005 98% 5005 99% 5005 100% 5005 (longest request)
Наконец, Starlette с блокирующим вызовом (Time.sleep (5)) вместо await enyncio.sleep (5):
This is ApacheBench, Version 2.3 <$Revision: 1874286 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient).....done Server Software: uvicorn Server Hostname: localhost Server Port: 8000 Document Path: / Document Length: 6 bytes Concurrency Level: 5 Time taken for tests: 25.036 seconds Complete requests: 5 Failed requests: 0 Total transferred: 695 bytes HTML transferred: 30 bytes Requests per second: 0.20 [#/sec] (mean) Time per request: 25035.827 [ms] (mean) Time per request: 5007.165 [ms] (mean, across all concurrent requests) Transfer rate: 0.03 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.6 1 2 Processing: 5009 17023 6716.0 20026 20026 Waiting: 5007 17022 6716.5 20026 20026 Total: 5009 17024 6716.6 20028 20028 Percentage of the requests served within a certain time (ms) 50% 20027 66% 20028 75% 20028 80% 20028 90% 20028 95% 20028 98% 20028 99% 20028 100% 20028 (longest request)
Как видите, это почти так же, как сокол.
С Web API, все редко это просто, и существуют и другие преимущества, которые можно найти с уровнем зрелости экосистемы (синхронные библиотеки, имели более длительное время, чтобы получить правильные вещи), и никогда не должен пропустить простоту использования, чтобы убедиться, что сделаны правильно, но общая идея выглядит следующим образом:
Большая часть «скорости» Async IO происходит от возможности выполнять дополнительную работу, а другая работа выполняется, но не нужно внимания. В простых тестах выше, это обслуживает базовый запрос Get, но это относится везде.
Оригинал: “https://dev.to/sjsadowski/speed-in-async-web-frameworks-196m”