Skip to content

Commit

Permalink
Add blacksheep in benchmarks and update benchmarks with latest versio…
Browse files Browse the repository at this point in the history
…n of each library
  • Loading branch information
Ananto30 committed Jun 28, 2024
1 parent eb5582c commit 67a73cc
Show file tree
Hide file tree
Showing 15 changed files with 447 additions and 24 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,15 @@ Compare the results! 👇

Framework | "hello world" (req/s) | 99% latency (ms) | redis save (req/s) | 99% latency (ms)
----------- | --------------------- | ---------------- | ------------------ | ----------------
aiohttp | 14391.38 | 10.96 | 9470.74 | 12.94
aiozmq | 15121.86 | 9.42 | 5904.84 | 21.57
fastApi | 9590.96 | 18.31 | 6669.81 | 24.41
sanic | 18790.49 | 8.69 | 12259.29 | 13.52
zero(sync) | 27842.80 | 4.34 | 18401.14 | 6.58
zero(async) | 27958.60 | 3.55 | 20131.83 | 6.21
aiohttp | 14949.57 | 8.91 | 9753.87 | 13.75
aiozmq | 13844.67 | 9.55 | 5239.14 | 30.92
blacksheep | 32967.27 | 3.03 | 18010.67 | 6.79
fastApi | 13154.96 | 9.07 | 8369.87 | 15.91
sanic | 18793.08 | 5.88 | 12739.37 | 8.78
zero(sync) | 28471.47 | 4.12 | 18114.84 | 6.69
zero(async) | 29012.03 | 3.43 | 20956.48 | 5.80

Seems like blacksheep is the aster on hello world, but in more complex operations like saving to redis, zero is the winner! 🏆

# Roadmap 🗺

Expand Down
17 changes: 13 additions & 4 deletions benchmarks/dockerize/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
SHELL := /bin/bash
benchmark-aiohttp:
cd aiohttp && ( \
docker-compose up -d gateway server redis; \
docker-compose up -d --build gateway server redis; \
sleep 2; \
docker-compose up wrk-hello; \
docker-compose up wrk-order; \
docker-compose down; \
)

benchmark-blacksheep:
cd blacksheep && ( \
docker-compose up -d --build gateway server redis; \
sleep 2; \
docker-compose up wrk-hello; \
docker-compose up wrk-order; \
Expand All @@ -10,7 +19,7 @@ benchmark-aiohttp:

benchmark-aiozmq:
cd aiozmq && ( \
docker-compose up -d gateway server redis; \
docker-compose up -d --build gateway server redis; \
sleep 2; \
docker-compose up wrk-hello; \
docker-compose up wrk-order; \
Expand All @@ -19,7 +28,7 @@ benchmark-aiozmq:

benchmark-fastapi:
cd fast_api && ( \
docker-compose up -d gateway server redis; \
docker-compose up -d --build gateway server redis; \
sleep 2; \
docker-compose up wrk-hello; \
docker-compose up wrk-order; \
Expand All @@ -28,7 +37,7 @@ benchmark-fastapi:

benchmark-sanic:
cd sanic && ( \
docker-compose up -d gateway server redis; \
docker-compose up -d --build gateway server redis; \
sleep 2; \
docker-compose up wrk-hello; \
docker-compose up wrk-order; \
Expand Down
4 changes: 4 additions & 0 deletions benchmarks/dockerize/blacksheep/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM python:3.9-slim

COPY . .
RUN pip install -r requirements.txt
35 changes: 35 additions & 0 deletions benchmarks/dockerize/blacksheep/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: '3'

services:
gateway:
build: .
container_name: gateway
command: uvicorn gateway:app --host 0.0.0.0 --port 8000 --workers 8 --log-level warning
ports:
- "8000:8000"
depends_on:
- server
- redis
server:
build: .
container_name: server
command: uvicorn server:app --host 0.0.0.0 --port 8011 --workers 8 --log-level warning
ports:
- "8011:8011"
# cpus: '0.50'
# mem_limit: 256m
redis:
image: eqalpha/keydb:latest
container_name: redis
ports:
- "6379:6379"
wrk-hello:
image: skandyla/wrk
command: -t 8 -c 64 -d 15s --latency http://gateway:8000/hello
depends_on:
- gateway
wrk-order:
image: skandyla/wrk
command: -t 8 -c 64 -d 15s --latency http://gateway:8000/order
depends_on:
- gateway
48 changes: 48 additions & 0 deletions benchmarks/dockerize/blacksheep/gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import logging
from typing import Optional

from blacksheep import Application, JSONContent
from blacksheep import json as json_resp
from blacksheep import text
from blacksheep.client import ClientSession

app = Application()
get = app.router.get

logger = logging.getLogger(__name__)

try:
import uvloop

uvloop.install()
except ImportError:
logger.warning("Cannot use uvloop")

session: Optional[ClientSession] = None


@get("/hello")
async def hello():
global session
if session is None:
session = ClientSession()

resp = await session.get("http://server:8011/hello")
txt = await resp.text()
return text(txt)


@get("/order")
async def order():
global session
if session is None:
session = ClientSession()

content = JSONContent(
data={
"user_id": "1",
"items": ["apple", "python"],
}
)
resp = await session.post("http://server:8011/order", content=content)
return json_resp(await resp.json())
7 changes: 7 additions & 0 deletions benchmarks/dockerize/blacksheep/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
blacksheep
uvicorn
PyJWT
aioredis
uvloop
msgpack
redis
85 changes: 85 additions & 0 deletions benchmarks/dockerize/blacksheep/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import logging
import uuid
from dataclasses import dataclass
from datetime import datetime
from typing import List

from blacksheep import Application, FromJSON, json, text
from shared import Order, OrderResp, OrderStatus, async_save_order

app = Application()
get = app.router.get
post = app.router.post

logger = logging.getLogger(__name__)

try:
import uvloop

uvloop.install()
except ImportError:
logger.warning("Cannot use uvloop")


@get("/hello")
async def hello():
return text("hello world")


@get("/order")
async def get_order():
saved_order = await async_save_order(
Order(
id=str(uuid.uuid4()),
created_by="1",
items=["apple", "python"],
created_at=datetime.now().isoformat(),
status=OrderStatus.INITIATED,
)
)

resp = OrderResp(
saved_order.id,
saved_order.status,
saved_order.items,
)
return json(
{
"id": resp.order_id,
"status": resp.status,
"items": resp.items,
}
)


@dataclass
class OrderReq:
user_id: str
items: List[str]


@post("/order")
async def order(req: FromJSON[OrderReq]):
body = req.value
saved_order = await async_save_order(
Order(
id=str(uuid.uuid4()),
created_by=body.user_id,
items=body.items,
created_at=datetime.now().isoformat(),
status=OrderStatus.INITIATED,
)
)

resp = OrderResp(
saved_order.id,
saved_order.status,
saved_order.items,
)
return json(
{
"id": resp.order_id,
"status": resp.status,
"items": resp.items,
}
)
122 changes: 122 additions & 0 deletions benchmarks/dockerize/blacksheep/shared.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from dataclasses import dataclass
from datetime import datetime

import aioredis
import msgpack
import redis


@dataclass
class Btype:
def pack(self):
return msgpack.packb(Btype.get_all_vars(self))

@classmethod
def unpack(cls, d):
return cls(**msgpack.unpackb(d, raw=False))

@staticmethod
def get_all_vars(obj):
values = vars(obj)
for k, v in values.items():
if isinstance(v, Btype):
values[k] = vars(v)
return values


class OrderStatus:
INITIATED = 0
PACKING = 1
SHIPPED = 2
DELIVERED = 3


class Order(Btype):
# cache
orders = {}

def __init__(self, id, items, created_by, created_at, status, updated_at=None):
self.id = id
self.items = items
self.created_by = created_by
self.created_at = created_at
self.status = status
self.updated_at = updated_at


@dataclass
class OrderResp(Btype):
order_id: str
status: int
items: list


@dataclass
class CreateOrderReq(Btype):
user_id: str
items: list


class SingletonMeta(type):
_instance = None

def __call__(self):
if self._instance is None:
self._instance = super().__call__()
return self._instance


class AsyncRedisClient(metaclass=SingletonMeta):
def __init__(self):
self.client = aioredis.from_url("redis://redis:6379/0")

async def set(self, key, val):
await self.client.set(key, val)

async def get(self, key):
return await self.client.get(key)


async def async_save_order(order: Order) -> Order:
order.updated_at = datetime.now().isoformat()
r = AsyncRedisClient()
await r.set(order.id, order.pack())
return order


async def async_get_order(id: str) -> Order:
r = AsyncRedisClient()
return Order.unpack(await r.get(id))


class SingletonMeta(type):
_instance = None

def __call__(self):
if self._instance is None:
self._instance = super().__call__()
return self._instance


class RedisClient(metaclass=SingletonMeta):
def __init__(self):
self.host = "0.0.0.0"
self.client = redis.StrictRedis().from_url("redis://redis:6379/0")

def set(self, key, val):
self.client.set(key, val)

def get(self, key):
return self.client.get(key)


def save_order(order: Order) -> Order:
order.updated_at = datetime.now().isoformat()
r = RedisClient()
r.set(order.id, order.pack())
return order


def get_order(id: str) -> Order:
r = RedisClient()
return Order.unpack(r.get(id))
4 changes: 2 additions & 2 deletions benchmarks/dockerize/sanic/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


@app.route("/hello")
async def test(request):
async def hello(request):
global session
if session is None:
session = ClientSession()
Expand All @@ -31,7 +31,7 @@ async def test(request):


@app.route("/order")
async def test(request):
async def order(request):
global session
if session is None:
session = ClientSession()
Expand Down
Loading

0 comments on commit 67a73cc

Please sign in to comment.