Skip to content

Instantly share code, notes, and snippets.

@rghv404
Last active July 25, 2024 01:24
Show Gist options
  • Save rghv404/b38a3bcf00ecbfeff64a64d8d67c4dc2 to your computer and use it in GitHub Desktop.
Save rghv404/b38a3bcf00ecbfeff64a64d8d67c4dc2 to your computer and use it in GitHub Desktop.
Quick benchmark of serial vs async vs async_httpx vs threadpool vs processpool
import asyncio
import time
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from contextlib import contextmanager
import httpx
import requests
URL = "https://invitationhomes.com"
@contextmanager
def timeit(*x):
print("\nstarting", x, flush=True)
start = time.time()
try:
yield
finally:
print(f"done {x} in {time.time() - start:.2f} seconds", flush=True)
def normal_requests(unused=None) -> int:
resp = requests.get(URL)
resp.raise_for_status()
return resp.status_code
def do_normal_requests(N):
return [normal_requests() for _ in range(N)]
async def async_requests() -> int:
resp = requests.get(URL)
resp.raise_for_status()
return resp.status_code
async def do_async_requests(N):
return await asyncio.gather(*(async_requests() for _ in range(N)))
async def async_httpx() -> int:
async with httpx.AsyncClient(follow_redirects=True, verify=False, timeout=None) as client:
response = await client.get(URL)
response.raise_for_status()
return response.status_code
async def do_async_httpx(N):
return await asyncio.gather(*(async_httpx() for _ in range(N)))
def do_thread_pool_executor(N, W):
with ThreadPoolExecutor(max_workers=W) as executor:
return [x for x in executor.map(normal_requests, range(N))]
def do_process_pool_executor(N, W):
with ProcessPoolExecutor(max_workers=W) as executor:
return [x for x in executor.map(normal_requests, range(N))]
def main():
N = 20
LOOP = 3
print(f"For each below, loop {LOOP} times, each loop {N} batch size.")
with timeit("serial"):
for _ in range(LOOP):
print(">", end="", flush=True)
(do_normal_requests(N))
with timeit("async but normal requests"):
for _ in range(LOOP):
print(">", end="", flush=True)
(asyncio.run(do_async_requests(N)))
with timeit("async httpx"):
for _ in range(LOOP):
print(">", end="", flush=True)
(asyncio.run(do_async_httpx(N)))
Ws = [1, 2, 3, 5, 10, 15, 20]
for W in Ws:
with timeit("thread pool executor", W):
for _ in range(LOOP):
print(">", end="", flush=True)
(do_thread_pool_executor(N, W))
for W in Ws:
with timeit("process pool executor", W):
for _ in range(LOOP):
print(">", end="", flush=True)
(do_process_pool_executor(N, W))
if __name__ == "__main__":
main()
@rghv404
Copy link
Author

rghv404 commented Jul 25, 2024

@rghv404
Copy link
Author

rghv404 commented Jul 25, 2024

Macbook M1 pro 2021
RAM 16 GB

For each below, loop 3 times, each loop 20 batch size.

starting ('serial',)
>>>done ('serial',) in 39.92 seconds

starting ('async but normal requests',)
>>>done ('async but normal requests',) in 33.38 seconds

starting ('async httpx',)
>>>done ('async httpx',) in 4.21 seconds

starting ('thread pool executor', 1)
>>>done ('thread pool executor', 1) in 31.48 seconds

starting ('thread pool executor', 2)
>>>done ('thread pool executor', 2) in 16.38 seconds

starting ('thread pool executor', 3)
>>>done ('thread pool executor', 3) in 9.51 seconds

starting ('thread pool executor', 5)
>>>done ('thread pool executor', 5) in 6.40 seconds

starting ('thread pool executor', 10)
>>>done ('thread pool executor', 10) in 4.63 seconds

starting ('thread pool executor', 15)
>>>done ('thread pool executor', 15) in 3.25 seconds

starting ('thread pool executor', 20)
>>>done ('thread pool executor', 20) in 4.15 seconds

starting ('process pool executor', 1)
>>>done ('process pool executor', 1) in 26.12 seconds

starting ('process pool executor', 2)
>>>done ('process pool executor', 2) in 16.45 seconds

starting ('process pool executor', 3)
>>>done ('process pool executor', 3) in 11.29 seconds

starting ('process pool executor', 5)
>>>done ('process pool executor', 5) in 8.49 seconds

starting ('process pool executor', 10)
>>>done ('process pool executor', 10) in 7.78 seconds

starting ('process pool executor', 15)
>>>done ('process pool executor', 15) in 8.38 seconds

starting ('process pool executor', 20)
>>>done ('process pool executor', 20) in 10.90 seconds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment