Skip to content

Commit

Permalink
refactor performance script
Browse files Browse the repository at this point in the history
  • Loading branch information
sonic182 committed Aug 14, 2024
1 parent 6961576 commit dd47a04
Showing 1 changed file with 56 additions and 89 deletions.
145 changes: 56 additions & 89 deletions tests/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@

try:
import uvloop

uvloop.install()
except Exception:
except ImportError:
pass


Expand All @@ -36,62 +35,52 @@ def is_tool(name):


async def app(scope, receive, send):
"""Simple ASGI app."""
assert scope["type"] == "http"
res = b"foo"
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
[b"content-length", b"%d" % len(res)],
],
}
)
await send(
{
"type": "http.response.body",
"body": res,
}
)
await send({
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
[b"content-length", b"%d" % len(res)],
],
})
await send({
"type": "http.response.body",
"body": res,
})


async def start_dummy_server(port):
"""Start dummy server."""
host = "0.0.0.0"

config = Config(app, host=host, port=port, workers=2, log_level="warning")
config = Config(app, host="0.0.0.0", port=port, workers=2, log_level="warning")
server = Server(config=config)

await server.serve()


async def timeit_coro(func, *args, **kwargs):
"""To time stuffs."""
repeat = kwargs.pop("repeat", 1000)
async def timeit_coro(func, *args, repeat=1000, **kwargs):
"""Measure the time taken for repeated asynchronous tasks."""
before = datetime.now()
# Concurrent coroutines
await asyncio.gather(*[func(*args, **kwargs) for _ in range(repeat)])
after = datetime.now()
return (after - before) / timedelta(milliseconds=1)


async def performance_aiohttp(url, concurrency):
"""Test aiohttp performance."""
async with aiohttp.ClientSession(
connector=aiohttp.TCPConnector(limit=concurrency)
) as session:
return await timeit_coro(session.get, (url))
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=concurrency)) as session:
return await timeit_coro(session.get, url)


async def performance_aiosonic(url, concurrency, pool_cls=None, timeouts=None):
"""Test aiohttp performance."""
"""Test aiosonic performance."""
client = aiosonic.HTTPClient(TCPConnector(pool_size=concurrency, pool_cls=pool_cls))
return await timeit_coro(client.get, url, timeouts=timeouts)


async def performance_httpx(url, concurrency, pool_cls=None):
"""Test aiohttp performance."""
async def performance_httpx(url, concurrency):
"""Test httpx performance."""
async with httpx.AsyncClient() as client:
return await timeit_coro(client.get, url)

Expand All @@ -104,72 +93,42 @@ def timeit_requests(url, concurrency, repeat=1000):
)
session.mount("http://", adapter)
with futures.ThreadPoolExecutor(concurrency) as executor:
to_wait = []
futures_list = [executor.submit(session.get, url) for _ in range(repeat)]
before = datetime.now()
for _ in range(repeat):
to_wait.append(executor.submit(session.get, url))
for fut in to_wait:
for fut in futures.as_completed(futures_list):
fut.result()
after = datetime.now()
return (after - before) / timedelta(milliseconds=1)


def do_tests(url):
async def do_tests(url):
"""Start benchmark."""
print("doing tests...")
print("Running performance tests...")
concurrency = 25

# aiohttp
res1 = asyncio.run(performance_aiohttp(url, concurrency))

# aiosonic
res2 = asyncio.run(performance_aiosonic(url, concurrency))

# requests
res3 = timeit_requests(url, concurrency)
results = {}

# aiosonic cyclic
res4 = asyncio.run(performance_aiosonic(url, concurrency, pool_cls=CyclicQueuePool))
results["aiohttp"] = await performance_aiohttp(url, concurrency)
results["aiosonic"] = await performance_aiosonic(url, concurrency)
results["requests"] = timeit_requests(url, concurrency)
results["aiosonic_cyclic"] = await performance_aiosonic(url, concurrency, pool_cls=CyclicQueuePool)

# httpx
httpx_exc = False
res5 = None
try:
res5 = asyncio.run(performance_httpx(url, concurrency))
results["httpx"] = await performance_httpx(url, concurrency)
except Exception as exc:
httpx_exc = exc
print("httpx did break with: " + str(exc))
# results["httpx_error"] = str(exc)
print(f"httpx encountered an error: {exc}")

to_print = {
"aiosonic": "1000 requests in %.2f ms" % res2,
"aiosonic cyclic": "1000 requests in %.2f ms" % res4,
"aiohttp": "1000 requests in %.2f ms" % res1,
"requests": "1000 requests in %.2f ms" % res3,
}
print(json.dumps({k: f"1000 requests in {v:.2f} ms" for k, v in results.items() if not k.endswith('_error')}, indent=4))

if not httpx_exc:
to_print.update({"httpx": "1000 requests in %.2f ms" % res5})
if "httpx" in results:
print(f"aiosonic is {((results['httpx'] / results['aiosonic']) - 1) * 100:.2f}% faster than httpx")

print(json.dumps(to_print, indent=True))
print(f"aiosonic is {((results['aiohttp'] / results['aiosonic']) - 1) * 100:.2f}% faster than aiohttp")
print(f"aiosonic is {((results['requests'] / results['aiosonic']) - 1) * 100:.2f}% faster than requests")
print(f"aiosonic is {((results['aiosonic_cyclic'] / results['aiosonic']) - 1) * 100:.2f}% faster than aiosonic cyclic")

print("aiosonic is %.2f%% faster than aiohttp" % (((res1 / res2) - 1) * 100))
print("aiosonic is %.2f%% faster than requests" % (((res3 / res2) - 1) * 100))
print(
"aiosonic is %.2f%% faster than aiosonic cyclic" % (((res4 / res2) - 1) * 100)
)

res = [
["aiohttp", res1],
["aiosonic", res2],
["requests", res3],
["aiosonic_cyclic", res4],
]

if not httpx_exc:
print("aiosonic is %.2f%% faster than httpx" % (((res5 / res2) - 1) * 100))
res.append(["httpx", res5])

return res
return results


def start_server(port):
Expand All @@ -181,28 +140,36 @@ def start_server(port):


def main():
"""Start."""
"""Start the performance test."""
port = random.randint(1000, 9000)
url = "http://0.0.0.0:%d" % port
url = f"http://0.0.0.0:{port}"
process = Process(target=start_server, args=(port,))
process.start()

max_wait = datetime.now() + timedelta(seconds=5)
while True:
while datetime.now() < max_wait:
try:
with urlopen(url) as response:
response.read()
break
except URLError:
sleep(1)
if datetime.now() > max_wait:
raise
else:
print("Server did not start in time.")
process.terminate()
return

try:
res = do_tests(url)
assert "aiosonic" in sorted(res, key=lambda x: x[1])[0][0]
res = asyncio.run(do_tests(url))

# Check if any results are valid and proceed
fastest_client = sorted(res.items(), key=lambda x: x[1])[0][0]
assert "aiosonic" in fastest_client

finally:
process.terminate()



if __name__ == "__main__":
main()

0 comments on commit dd47a04

Please sign in to comment.