v14
authorSam Mirazi <sasan345@gmail.com>
Mon, 2 Jun 2025 04:17:51 +0000 (21:17 -0700)
committerSam Mirazi <sasan345@gmail.com>
Mon, 2 Jun 2025 04:17:51 +0000 (21:17 -0700)
app_fastapi/__pycache__/app.cpython-312.pyc
run_benchmark_table.py [new file with mode: 0644]
show_benchmark_table.py [deleted file]

index b0f8d18f21c68828675227d10e6ed8b15d9c6294..1e2f5cebeeafec93fbd86c8d6bfd34f46d91d960 100644 (file)
Binary files a/app_fastapi/__pycache__/app.cpython-312.pyc and b/app_fastapi/__pycache__/app.cpython-312.pyc differ
diff --git a/run_benchmark_table.py b/run_benchmark_table.py
new file mode 100644 (file)
index 0000000..0bb3e7f
--- /dev/null
@@ -0,0 +1,240 @@
+import subprocess
+import time
+import re
+import requests  # pip install requests
+from rich.console import Console
+from rich.table import Table
+import sys
+import os
+
+# --- Configuration ------------------------------------------------------
+FLASK_SERVER_URL = "http://127.0.0.1:3000/"
+FASTAPI_SERVER_URL = "http://127.0.0.1:8000/"
+BENCHMARK_SCRIPT_PATH = "benchmark/run_benchmark.py"
+NUM_REQUESTS_EXPECTED = 100
+PYTHON_EXE = sys.executable
+
+# ------------------------------------------------------------------------
+console = Console()
+
+# -------------------------- helpers -------------------------------------
+def start_server(command_args, health_check_url, server_name, cwd=None):
+    """Start server and wait until a 200 health check is returned."""
+    console.print(f"[yellow]Starting {server_name} server…[/yellow]")
+
+    # --- STREAM HANDLING: inherit console so the child can always write
+    popen_kwargs = dict(cwd=cwd, text=True,
+                        stdout=subprocess.DEVNULL,
+                        stderr=subprocess.STDOUT)
+
+    # run either as  "python -m uvicorn ..."  or plain exe
+    if "uvicorn" in command_args[0] and not command_args[0].endswith(".exe"):
+        process = subprocess.Popen([PYTHON_EXE, "-m"] + command_args, **popen_kwargs)
+    else:
+        process = subprocess.Popen(command_args, **popen_kwargs)
+
+    max_wait = 30
+    start_t = time.time()
+    while time.time() - start_t < max_wait:
+        try:
+            if requests.get(health_check_url, timeout=3).status_code == 200:
+                console.print(f"[green]{server_name} ready.[/green]")
+                return process
+        except requests.RequestException:
+            time.sleep(0.3)
+    console.print(f"[red]{server_name} failed to start within {max_wait}s.[/red]")
+    process.terminate()
+    return None
+
+def stop_server(proc, name):
+    if not proc:
+        return
+    console.print(f"[yellow]Stopping {name}…[/yellow]")
+    proc.terminate()
+    try:
+        proc.wait(timeout=8)
+    except subprocess.TimeoutExpired:
+        proc.kill()
+    console.print(f"[green]{name} stopped.[/green]")
+
+def run_benchmark_script(framework_arg):
+    console.print(f"Running benchmark for [bold]{framework_arg}[/bold]…")
+    cmd = [PYTHON_EXE, BENCHMARK_SCRIPT_PATH, framework_arg]
+
+    if framework_arg.lower() == "flask":
+        final_summary_line = None
+        requests_done_count = 0
+        progress_line_printed = False
+        try:
+            # Ensure encoding is specified for Popen for consistent text handling
+            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True, encoding='utf-8')
+            
+            if process.stdout:
+                for line in iter(process.stdout.readline, ''):
+                    line = line.strip()
+                    if not line: 
+                        continue
+
+                    if line.startswith("REQ_STATUS:"):
+                        requests_done_count += 1
+                        # Using carriage return to update the line in place
+                        print(f"\rFlask progress: Handled {requests_done_count}/{NUM_REQUESTS_EXPECTED} requests...", end="", flush=True)
+                        progress_line_printed = True
+                    elif line.startswith("[DIAG-BRB-FLASK]"):
+                        if progress_line_printed:
+                            # Clear the progress line before printing diagnostic output
+                            print("\r" + " " * 80 + "\r", end="", flush=True) 
+                        print(line, flush=True) # Print diagnostic line
+                        if progress_line_printed:
+                            # Reprint the progress line after diagnostic output
+                            print(f"\rFlask progress: Handled {requests_done_count}/{NUM_REQUESTS_EXPECTED} requests...", end="", flush=True)
+                    elif "Final Flask benchmark summary:" in line:
+                        final_summary_line = line
+                        if progress_line_printed:
+                             # Clear the progress line before finishing
+                            print("\r" + " " * 80 + "\r", end="", flush=True)
+                        # The summary line itself will be printed by the main logic if needed, or parsed
+
+                process.stdout.close()
+            
+            # After the loop, if progress was printed, clear it finally
+            # This handles cases where the process ends without a final summary line immediately after progress
+            if progress_line_printed and not final_summary_line:
+                 print("\r" + " " * 80 + "\r", end="", flush=True)
+
+            stderr_output_list = []
+            if process.stderr:
+                for line in iter(process.stderr.readline, ''):
+                    line = line.strip()
+                    if line:
+                        stderr_output_list.append(line)
+                process.stderr.close()
+
+            process.wait(timeout=600) 
+
+            if process.returncode != 0:
+                console.print(f"[red]{framework_arg} benchmark script failed with return code {process.returncode}[/red]")
+                if stderr_output_list:
+                    console.print("[red]STDERR:[/red]")
+                    for err_line in stderr_output_list:
+                        console.print(f"[red]{err_line}[/red]")
+                return None
+            
+            if final_summary_line:
+                return final_summary_line
+            else:
+                console.print(f"[red]Could not find the final summary line for {framework_arg} in Popen benchmark output.[/red]")
+                if stderr_output_list:
+                    console.print("[red]STDERR output during Popen execution was:[/red]")
+                    for err_line in stderr_output_list:
+                        console.print(f"[red]{err_line}[/red]")
+                return None
+
+        except subprocess.TimeoutExpired:
+            console.print(f"[red]Benchmark for {framework_arg} (Popen path) timed out.[/red]")
+            if process.poll() is None: # Check if process is still running
+                process.kill()
+                process.wait()
+            return None
+        except Exception as e:
+            console.print(f"[red]An unexpected error occurred while running Popen benchmark for {framework_arg}: {e}[/red]")
+            return None
+            
+    else:  # For FastAPI or any other framework not needing live progress
+        try:
+            result = subprocess.run(cmd, text=True, capture_output=True, timeout=600, check=False, encoding='utf-8')
+            if result.returncode != 0:
+                console.print(f"[red]{framework_arg} benchmark failed with subprocess.run.[/red]")
+                if result.stderr:
+                    console.print(f"STDERR:\n{result.stderr.strip()}")
+                return None
+            
+            if result.stdout and result.stdout.strip():
+                lines = result.stdout.strip().splitlines()
+                if lines:
+                    return lines[-1] # Return the last line, expected to be the summary
+                else:
+                    console.print(f"[red]No lines in stdout from {framework_arg} benchmark script (subprocess.run path).[/red]")
+                    return None
+            else:
+                console.print(f"[red]No stdout from {framework_arg} benchmark script (subprocess.run path).[/red]")
+                if result.stderr and result.stderr.strip():
+                     console.print(f"STDERR:\n{result.stderr.strip()}")
+                return None
+        except subprocess.TimeoutExpired:
+            console.print(f"[red]Benchmark for {framework_arg} (subprocess.run path) timed out.[/red]")
+            return None
+        except Exception as e:
+            console.print(f"[red]An unexpected error occurred while running subprocess.run benchmark for {framework_arg}: {e}[/red]")
+            return None
+
+def parse_benchmark(line):
+    m = re.search(r"(\d+)/(\d+) successful requests in ([\d.]+) seconds", line)
+    if not m:
+        return None
+    succ, total, tsec = map(float, m.groups())
+    return {"successful": f"{int(succ)}/{int(total)}", "total_time": tsec}
+
+def display_table(rows):
+    tbl = Table(title="Benchmark Summary", show_lines=True, header_style="bold magenta")
+    tbl.add_column("Framework", style="cyan")
+    tbl.add_column("Server", style="white")
+    tbl.add_column("Delay", style="green")
+    tbl.add_column("#Reqs", justify="right")
+    tbl.add_column("Success", justify="right")
+    tbl.add_column("Total s", justify="right", style="yellow")
+    tbl.add_column("Avg s/req", justify="right", style="blue")
+    for r in rows:
+        avg = r["total_time"] / NUM_REQUESTS_EXPECTED
+        tbl.add_row(r["framework"], r["config"], r["delay"],
+                    str(NUM_REQUESTS_EXPECTED), r["successful"],
+                    f"{r['total_time']:.2f}", f"{avg:.3f}")
+    console.print(tbl)
+
+# --------------------------- scenarios ----------------------------------
+SCENARIOS = [
+    {
+        "name": "FastAPI",
+        "config": "Uvicorn, async",
+        "delay": "0.3 s asyncio.sleep",
+        "cmd": ["uvicorn", "app_fastapi.app:app", "--host", "0.0.0.0",
+                "--port", "8000", "--log-level", "warning"],
+        "url": FASTAPI_SERVER_URL,
+        "bench_arg": "fastapi",
+    },
+    {
+        "name": "Flask",
+        "config": "Single-threaded",
+        "delay": "0.3 s time.sleep",
+        "cmd": [PYTHON_EXE, "app_flask/flask_application.py"],
+        "url": FLASK_SERVER_URL,
+        "bench_arg": "flask",
+    }
+]
+
+# ----------------------------- main -------------------------------------
+if __name__ == "__main__":
+    console.print("[bold underline]Automated Web Framework Benchmark[/bold underline]\n")
+    results = []
+    root = os.getcwd()
+
+    for i, sc in enumerate(SCENARIOS, 1):
+        console.rule(f"[cyan]Scenario {i}/{len(SCENARIOS)} – {sc['name']}[/cyan]")
+        srv = start_server(sc["cmd"], sc["url"], sc["name"], cwd=root)
+        if not srv:
+            continue
+        try:
+            if sc["name"].lower() == "flask":
+                time.sleep(2)  # tiny grace period
+            line = run_benchmark_script(sc["bench_arg"])
+            parsed = parse_benchmark(line) if line else None
+            if parsed:
+                results.append({"framework": sc["name"], "config": sc["config"],
+                                "delay": sc["delay"], **parsed})
+        finally:
+            stop_server(srv, sc["name"])
+        console.print()
+
+    if results:
+        display_table(results)
+    console.print("\n[bold]Benchmark run finished.[/bold]") 
\ No newline at end of file
diff --git a/show_benchmark_table.py b/show_benchmark_table.py
deleted file mode 100644 (file)
index 0bb3e7f..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-import subprocess
-import time
-import re
-import requests  # pip install requests
-from rich.console import Console
-from rich.table import Table
-import sys
-import os
-
-# --- Configuration ------------------------------------------------------
-FLASK_SERVER_URL = "http://127.0.0.1:3000/"
-FASTAPI_SERVER_URL = "http://127.0.0.1:8000/"
-BENCHMARK_SCRIPT_PATH = "benchmark/run_benchmark.py"
-NUM_REQUESTS_EXPECTED = 100
-PYTHON_EXE = sys.executable
-
-# ------------------------------------------------------------------------
-console = Console()
-
-# -------------------------- helpers -------------------------------------
-def start_server(command_args, health_check_url, server_name, cwd=None):
-    """Start server and wait until a 200 health check is returned."""
-    console.print(f"[yellow]Starting {server_name} server…[/yellow]")
-
-    # --- STREAM HANDLING: inherit console so the child can always write
-    popen_kwargs = dict(cwd=cwd, text=True,
-                        stdout=subprocess.DEVNULL,
-                        stderr=subprocess.STDOUT)
-
-    # run either as  "python -m uvicorn ..."  or plain exe
-    if "uvicorn" in command_args[0] and not command_args[0].endswith(".exe"):
-        process = subprocess.Popen([PYTHON_EXE, "-m"] + command_args, **popen_kwargs)
-    else:
-        process = subprocess.Popen(command_args, **popen_kwargs)
-
-    max_wait = 30
-    start_t = time.time()
-    while time.time() - start_t < max_wait:
-        try:
-            if requests.get(health_check_url, timeout=3).status_code == 200:
-                console.print(f"[green]{server_name} ready.[/green]")
-                return process
-        except requests.RequestException:
-            time.sleep(0.3)
-    console.print(f"[red]{server_name} failed to start within {max_wait}s.[/red]")
-    process.terminate()
-    return None
-
-def stop_server(proc, name):
-    if not proc:
-        return
-    console.print(f"[yellow]Stopping {name}…[/yellow]")
-    proc.terminate()
-    try:
-        proc.wait(timeout=8)
-    except subprocess.TimeoutExpired:
-        proc.kill()
-    console.print(f"[green]{name} stopped.[/green]")
-
-def run_benchmark_script(framework_arg):
-    console.print(f"Running benchmark for [bold]{framework_arg}[/bold]…")
-    cmd = [PYTHON_EXE, BENCHMARK_SCRIPT_PATH, framework_arg]
-
-    if framework_arg.lower() == "flask":
-        final_summary_line = None
-        requests_done_count = 0
-        progress_line_printed = False
-        try:
-            # Ensure encoding is specified for Popen for consistent text handling
-            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True, encoding='utf-8')
-            
-            if process.stdout:
-                for line in iter(process.stdout.readline, ''):
-                    line = line.strip()
-                    if not line: 
-                        continue
-
-                    if line.startswith("REQ_STATUS:"):
-                        requests_done_count += 1
-                        # Using carriage return to update the line in place
-                        print(f"\rFlask progress: Handled {requests_done_count}/{NUM_REQUESTS_EXPECTED} requests...", end="", flush=True)
-                        progress_line_printed = True
-                    elif line.startswith("[DIAG-BRB-FLASK]"):
-                        if progress_line_printed:
-                            # Clear the progress line before printing diagnostic output
-                            print("\r" + " " * 80 + "\r", end="", flush=True) 
-                        print(line, flush=True) # Print diagnostic line
-                        if progress_line_printed:
-                            # Reprint the progress line after diagnostic output
-                            print(f"\rFlask progress: Handled {requests_done_count}/{NUM_REQUESTS_EXPECTED} requests...", end="", flush=True)
-                    elif "Final Flask benchmark summary:" in line:
-                        final_summary_line = line
-                        if progress_line_printed:
-                             # Clear the progress line before finishing
-                            print("\r" + " " * 80 + "\r", end="", flush=True)
-                        # The summary line itself will be printed by the main logic if needed, or parsed
-
-                process.stdout.close()
-            
-            # After the loop, if progress was printed, clear it finally
-            # This handles cases where the process ends without a final summary line immediately after progress
-            if progress_line_printed and not final_summary_line:
-                 print("\r" + " " * 80 + "\r", end="", flush=True)
-
-            stderr_output_list = []
-            if process.stderr:
-                for line in iter(process.stderr.readline, ''):
-                    line = line.strip()
-                    if line:
-                        stderr_output_list.append(line)
-                process.stderr.close()
-
-            process.wait(timeout=600) 
-
-            if process.returncode != 0:
-                console.print(f"[red]{framework_arg} benchmark script failed with return code {process.returncode}[/red]")
-                if stderr_output_list:
-                    console.print("[red]STDERR:[/red]")
-                    for err_line in stderr_output_list:
-                        console.print(f"[red]{err_line}[/red]")
-                return None
-            
-            if final_summary_line:
-                return final_summary_line
-            else:
-                console.print(f"[red]Could not find the final summary line for {framework_arg} in Popen benchmark output.[/red]")
-                if stderr_output_list:
-                    console.print("[red]STDERR output during Popen execution was:[/red]")
-                    for err_line in stderr_output_list:
-                        console.print(f"[red]{err_line}[/red]")
-                return None
-
-        except subprocess.TimeoutExpired:
-            console.print(f"[red]Benchmark for {framework_arg} (Popen path) timed out.[/red]")
-            if process.poll() is None: # Check if process is still running
-                process.kill()
-                process.wait()
-            return None
-        except Exception as e:
-            console.print(f"[red]An unexpected error occurred while running Popen benchmark for {framework_arg}: {e}[/red]")
-            return None
-            
-    else:  # For FastAPI or any other framework not needing live progress
-        try:
-            result = subprocess.run(cmd, text=True, capture_output=True, timeout=600, check=False, encoding='utf-8')
-            if result.returncode != 0:
-                console.print(f"[red]{framework_arg} benchmark failed with subprocess.run.[/red]")
-                if result.stderr:
-                    console.print(f"STDERR:\n{result.stderr.strip()}")
-                return None
-            
-            if result.stdout and result.stdout.strip():
-                lines = result.stdout.strip().splitlines()
-                if lines:
-                    return lines[-1] # Return the last line, expected to be the summary
-                else:
-                    console.print(f"[red]No lines in stdout from {framework_arg} benchmark script (subprocess.run path).[/red]")
-                    return None
-            else:
-                console.print(f"[red]No stdout from {framework_arg} benchmark script (subprocess.run path).[/red]")
-                if result.stderr and result.stderr.strip():
-                     console.print(f"STDERR:\n{result.stderr.strip()}")
-                return None
-        except subprocess.TimeoutExpired:
-            console.print(f"[red]Benchmark for {framework_arg} (subprocess.run path) timed out.[/red]")
-            return None
-        except Exception as e:
-            console.print(f"[red]An unexpected error occurred while running subprocess.run benchmark for {framework_arg}: {e}[/red]")
-            return None
-
-def parse_benchmark(line):
-    m = re.search(r"(\d+)/(\d+) successful requests in ([\d.]+) seconds", line)
-    if not m:
-        return None
-    succ, total, tsec = map(float, m.groups())
-    return {"successful": f"{int(succ)}/{int(total)}", "total_time": tsec}
-
-def display_table(rows):
-    tbl = Table(title="Benchmark Summary", show_lines=True, header_style="bold magenta")
-    tbl.add_column("Framework", style="cyan")
-    tbl.add_column("Server", style="white")
-    tbl.add_column("Delay", style="green")
-    tbl.add_column("#Reqs", justify="right")
-    tbl.add_column("Success", justify="right")
-    tbl.add_column("Total s", justify="right", style="yellow")
-    tbl.add_column("Avg s/req", justify="right", style="blue")
-    for r in rows:
-        avg = r["total_time"] / NUM_REQUESTS_EXPECTED
-        tbl.add_row(r["framework"], r["config"], r["delay"],
-                    str(NUM_REQUESTS_EXPECTED), r["successful"],
-                    f"{r['total_time']:.2f}", f"{avg:.3f}")
-    console.print(tbl)
-
-# --------------------------- scenarios ----------------------------------
-SCENARIOS = [
-    {
-        "name": "FastAPI",
-        "config": "Uvicorn, async",
-        "delay": "0.3 s asyncio.sleep",
-        "cmd": ["uvicorn", "app_fastapi.app:app", "--host", "0.0.0.0",
-                "--port", "8000", "--log-level", "warning"],
-        "url": FASTAPI_SERVER_URL,
-        "bench_arg": "fastapi",
-    },
-    {
-        "name": "Flask",
-        "config": "Single-threaded",
-        "delay": "0.3 s time.sleep",
-        "cmd": [PYTHON_EXE, "app_flask/flask_application.py"],
-        "url": FLASK_SERVER_URL,
-        "bench_arg": "flask",
-    }
-]
-
-# ----------------------------- main -------------------------------------
-if __name__ == "__main__":
-    console.print("[bold underline]Automated Web Framework Benchmark[/bold underline]\n")
-    results = []
-    root = os.getcwd()
-
-    for i, sc in enumerate(SCENARIOS, 1):
-        console.rule(f"[cyan]Scenario {i}/{len(SCENARIOS)} – {sc['name']}[/cyan]")
-        srv = start_server(sc["cmd"], sc["url"], sc["name"], cwd=root)
-        if not srv:
-            continue
-        try:
-            if sc["name"].lower() == "flask":
-                time.sleep(2)  # tiny grace period
-            line = run_benchmark_script(sc["bench_arg"])
-            parsed = parse_benchmark(line) if line else None
-            if parsed:
-                results.append({"framework": sc["name"], "config": sc["config"],
-                                "delay": sc["delay"], **parsed})
-        finally:
-            stop_server(srv, sc["name"])
-        console.print()
-
-    if results:
-        display_table(results)
-    console.print("\n[bold]Benchmark run finished.[/bold]") 
\ No newline at end of file