From 6d0aad52523d0282fa7c7c45b06a94b4b33bbf1c Mon Sep 17 00:00:00 2001 From: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com> Date: Mon, 13 May 2024 01:37:31 +0000 Subject: [PATCH] Improve: `fastapi_server.py` to be feature-complete with respect to UCall server --- examples/README.md | 19 ++++++++++++- examples/bench.py | 39 ++++++++++++++++---------- examples/{login => }/fastapi_server.py | 5 +++- examples/login/jsonrpc_client.go | 3 +- 4 files changed, 47 insertions(+), 19 deletions(-) rename examples/{login => }/fastapi_server.py (97%) diff --git a/examples/README.md b/examples/README.md index d7e2c8d..93e6c07 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,21 @@ -# UCall Examples and Benchmarks +# UCall Example & Benchmark + +This folder implements a group of different servers with identical functionality, but using different RPC frameworks, including: + +- FastAPI server in Python, compatible with WSGI: `fastapi_server.py` +- UCall server in Python: `ucall_server.py` +- UCall server in C++: `ucall_server.cpp` +- gRPC server in Python: `grpc_server.py` + +All of them implement identical endpoints: + +- `echo` - return back the payload it received for throughput benchmarks +- `validate_session` - lightweight operation on two integers, returning a boolean, to benchmark the request latency on tiny tasks +- `create_user` - more complex operation on flat dictionary input with strings and integers +- `validate_user_identity` - that validates arguments, raises exceptions, and returns complex nested objects +- `set` & `get` - key-value store operations, similar to Redis +- `resize` - batch-capable image processing operation for Base64-encoded images +- `dot_product` - batch-capable matrix vector-vector multiplication operation ## Echo and Summation diff --git a/examples/bench.py b/examples/bench.py index 6a6897e..bae53ca 100644 --- a/examples/bench.py +++ b/examples/bench.py @@ -18,27 +18,27 @@ class Stats: requests_failure: int = 0 mean_latency_secs: float = 0 total_secs: float = 0 - last_failure: str = '' + last_failure: str = "" @property def success_rate(self) -> float: return (self.requests_correct * 1.0 / self.requests) if self.requests else 1.0 def __repr__(self) -> str: - bandwidth = self.requests / \ - self.total_secs if self.total_secs > 0 else 0.0 - result = f''' + bandwidth = self.requests / self.total_secs if self.total_secs > 0 else 0.0 + result = f""" - Took: {self.total_secs:.1f} CPU seconds - Total exchanges: {self.requests:,} - Success rate: {self.success_rate:.3%} - Mean latency: {self.mean_latency_secs * 1e6:.1f} microseconds - Mean bandwidth: {bandwidth:.1f} requests/s - ''' + """ return I(result) def bench_serial( - callable, *, + callable, + *, requests_count: int = 100_000, seconds: float = 10, progress: bool = False, @@ -90,19 +90,21 @@ def bench_parallel( callable=callable, seconds=seconds, requests_count=requests_count, - progress=progress) + progress=progress, + ) - requests_correct = Value('i', 0) - requests_incorrect = Value('i', 0) - requests = Value('i', 0) - mean_latency_secs = Value('f', 0) + requests_correct = Value("i", 0) + requests_incorrect = Value("i", 0) + requests = Value("i", 0) + mean_latency_secs = Value("f", 0) def run(): stats = bench_serial( callable=callable, seconds=seconds, requests_count=requests_count, - progress=False) + progress=False, + ) requests_correct.value += stats.requests_correct requests_incorrect.value += stats.requests_incorrect requests.value += stats.requests @@ -129,9 +131,16 @@ def run(): ) -def main(class_name: str, *, threads: int = 1, requests: int = 100_000, seconds: float = 10, progress: bool = False): +def main( + class_name: str, + *, + threads: int = 1, + requests: int = 100_000, + seconds: float = 10, + progress: bool = False, +): script_dir = os.path.dirname(os.path.abspath(__file__)) - sys.path.append(f'{script_dir}/login') + sys.path.append(f"{script_dir}/login") class_ = locate(class_name) stats = bench_parallel( callable=class_(), @@ -143,5 +152,5 @@ def main(class_name: str, *, threads: int = 1, requests: int = 100_000, seconds: print(stats) -if __name__ == '__main__': +if __name__ == "__main__": fire.Fire(main) diff --git a/examples/login/fastapi_server.py b/examples/fastapi_server.py similarity index 97% rename from examples/login/fastapi_server.py rename to examples/fastapi_server.py index a891825..57a4864 100644 --- a/examples/login/fastapi_server.py +++ b/examples/fastapi_server.py @@ -30,7 +30,10 @@ async def create_user(age: int, name: str, avatar: str, bio: str): @app.post("/validate_user_identity") async def validate_user_identity( - user_id: int, name: str, age: float, access_token: str + user_id: int, + name: str, + age: float, + access_token: str, ): if age < 18: raise HTTPException(status_code=400, detail=f"{name} must be older than 18") diff --git a/examples/login/jsonrpc_client.go b/examples/login/jsonrpc_client.go index 7657e13..591d758 100644 --- a/examples/login/jsonrpc_client.go +++ b/examples/login/jsonrpc_client.go @@ -9,7 +9,7 @@ import ( "os" "time" "bytes" - "flag" + "flag" ) var( @@ -79,7 +79,6 @@ func main() { _, err = conn.Write([]byte(req)) } if err != nil { - //fmt.Printf("Write Error: %v\n", err) break }