今回は、asyncioをつかって非同期にhttpリクエストを投げる場合と、普通に同期的に投げる場合の速度を比較してみました。
リクエスト投げ先のサーバーは自分で用意したWebサーバ1つのみ。
FastAPIで単に文字列OKを返すものです。
検証準備
(1)aiohttpをインストールする。
# pip install aiohttp[speedups]
Collecting aiohttp[speedups]
Downloading aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.3 MB)
|████████████████████████████████| 1.3 MB 1.3 MB/s
Collecting aiosignal>=1.1.2
Downloading aiosignal-1.2.0-py3-none-any.whl (8.2 kB)
Collecting multidict<7.0,>=4.5
Downloading multidict-5.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (187 kB)
|████████████████████████████████| 187 kB 9.6 MB/s
Collecting yarl<2.0,>=1.0
Downloading yarl-1.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (308 kB)
|████████████████████████████████| 308 kB 39.0 MB/s
Collecting frozenlist>=1.1.1
Downloading frozenlist-1.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (210 kB)
|████████████████████████████████| 210 kB 41.4 MB/s
Collecting charset-normalizer<3.0,>=2.0
Downloading charset_normalizer-2.0.9-py3-none-any.whl (39 kB)
Collecting attrs>=17.3.0
Downloading attrs-21.2.0-py2.py3-none-any.whl (53 kB)
|████████████████████████████████| 53 kB 9.7 MB/s
Collecting async-timeout<5.0,>=4.0.0a3
Downloading async_timeout-4.0.2-py3-none-any.whl (5.8 kB)
Collecting aiodns; extra == "speedups"
Downloading aiodns-3.0.0-py3-none-any.whl (5.0 kB)
Collecting cchardet; extra == "speedups"
Downloading cchardet-2.1.7-cp38-cp38-manylinux2010_x86_64.whl (265 kB)
|████████████████████████████████| 265 kB 14.0 MB/s
Collecting Brotli; extra == "speedups"
Downloading Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl (357 kB)
|████████████████████████████████| 357 kB 18.1 MB/s
Collecting idna>=2.0
Using cached idna-3.3-py3-none-any.whl (61 kB)
Collecting pycares>=4.0.0
Downloading pycares-4.1.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (292 kB)
|████████████████████████████████| 292 kB 47.0 MB/s
Collecting cffi>=1.5.0
Downloading cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (446 kB)
|████████████████████████████████| 446 kB 37.0 MB/s
Collecting pycparser
Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB)
|████████████████████████████████| 118 kB 5.4 MB/s
Installing collected packages: frozenlist, aiosignal, multidict, idna, yarl, charset-normalizer, attrs, async-timeout, pycparser, cffi, pycares, aiodns, cchardet, Brotli, aiohttp
Successfully installed Brotli-1.0.9 aiodns-3.0.0 aiohttp-3.8.1 aiosignal-1.2.0 async-timeout-4.0.2 attrs-21.2.0 cchardet-2.1.7 cffi-1.15.0 charset-normalizer-2.0.9 frozenlist-1.2.0 idna-3.3 multidict-5.2.0 pycares-4.1.2 pycparser-2.21 yarl-1.7.2
(2)使用したソースコードはこちらです。
※ LOOP_COUNT
を変更してテストしました。
import aiohttp
import asyncio
import requests
import time
TEST_URL = "http://localhost:8000"
LOOP_COUNT = 10
async def async_http(i: int):
async with aiohttp.ClientSession() as session:
async with session.get(TEST_URL) as response:
# print(f"Status {i}:", response.status)
status = response.status
def send_async():
loop = asyncio.get_event_loop()
for i in range(LOOP_COUNT):
loop.run_until_complete(async_http(i))
def send_syc():
for i in range(LOOP_COUNT):
res = requests.get(TEST_URL)
# print(f"Status {i}:", res.status_code)
if __name__ == '__main__':
start = time.time()
send_async()
end = time.time()
print(f"Async time:{end - start} sec")
start = time.time()
send_syc()
end = time.time()
print(f"Sync time:{end - start} sec")
結果
結果としては、リクエストの数が少ないとむしろ遅い。が、リクエスト数が多い場合は早くなりました。
# python asyncio_test.py 10
Async time:0.021250486373901367 sec
Sync time:0.015967130661010742 sec
# python asyncio_test.py 100
Async time:0.15844058990478516 sec
Sync time:0.16807842254638672 sec
# python asyncio_test.py 1000
Async time:1.4027554988861084 sec
Sync time:1.5084638595581055 sec
他のWebサイトの情報を収集するクローラーでは、同じサーバーに複数のリクエストを同時に送るというよりは、異なるサーバーに複数のリクエストを同時に送るケースだと思いますが。
この場合は、結果の差分はもっと変わるかもしれませんね。
ではでは!