python asyncioで並列処理を実装してみた

pythonWeb開発

今回は、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サイトの情報を収集するクローラーでは、同じサーバーに複数のリクエストを同時に送るというよりは、異なるサーバーに複数のリクエストを同時に送るケースだと思いますが。

この場合は、結果の差分はもっと変わるかもしれませんね。

ではでは!

タイトルとURLをコピーしました