Update.
This commit is contained in:
Executable
+240
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tikker Performance Benchmark Script
|
||||
|
||||
Measures and reports performance metrics for all services.
|
||||
Generates benchmark reports with detailed statistics.
|
||||
"""
|
||||
|
||||
import time
|
||||
import json
|
||||
import statistics
|
||||
import sys
|
||||
from typing import Dict, List, Tuple
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
|
||||
class BenchmarkRunner:
|
||||
"""Run benchmarks against services."""
|
||||
|
||||
def __init__(self, base_url: str = "http://localhost", verbose: bool = False):
|
||||
self.base_url = base_url
|
||||
self.verbose = verbose
|
||||
self.results: Dict[str, List[float]] = {}
|
||||
|
||||
def _request(self, method: str, service_port: int, endpoint: str,
|
||||
json_data: Dict = None, timeout: int = 30) -> Tuple[int, float]:
|
||||
"""Make HTTP request and measure latency."""
|
||||
url = f"{self.base_url}:{service_port}{endpoint}"
|
||||
start = time.time()
|
||||
|
||||
try:
|
||||
if method.upper() == "GET":
|
||||
response = requests.get(url, timeout=timeout)
|
||||
else:
|
||||
response = requests.post(url, json=json_data, timeout=timeout)
|
||||
|
||||
elapsed = (time.time() - start) * 1000
|
||||
|
||||
if self.verbose:
|
||||
print(f" {method} {endpoint}: {elapsed:.2f}ms -> {response.status_code}")
|
||||
|
||||
return response.status_code, elapsed
|
||||
|
||||
except RequestException as e:
|
||||
elapsed = (time.time() - start) * 1000
|
||||
if self.verbose:
|
||||
print(f" {method} {endpoint}: {elapsed:.2f}ms -> ERROR: {e}")
|
||||
return 0, elapsed
|
||||
|
||||
def record(self, name: str, latency: float):
|
||||
"""Record latency measurement."""
|
||||
if name not in self.results:
|
||||
self.results[name] = []
|
||||
self.results[name].append(latency)
|
||||
|
||||
def benchmark_api(self, iterations: int = 10):
|
||||
"""Benchmark main API endpoints."""
|
||||
print("\n=== API Service Benchmark ===")
|
||||
|
||||
endpoints = [
|
||||
("GET", 8000, "/health", None, "health"),
|
||||
("GET", 8000, "/", None, "root"),
|
||||
("GET", 8000, "/api/stats/daily", None, "daily_stats"),
|
||||
("GET", 8000, "/api/words/top?limit=10", None, "top_words"),
|
||||
]
|
||||
|
||||
for i in range(iterations):
|
||||
if i > 0 and i % (iterations // 4) == 0:
|
||||
print(f" Progress: {i}/{iterations}")
|
||||
|
||||
for method, port, endpoint, _, name in endpoints:
|
||||
status, latency = self._request(method, port, endpoint, json_data)
|
||||
if status in [200, 503]:
|
||||
self.record(f"api_{name}", latency)
|
||||
|
||||
def benchmark_ai(self, iterations: int = 5):
|
||||
"""Benchmark AI service."""
|
||||
print("\n=== AI Service Benchmark ===")
|
||||
|
||||
payload = {
|
||||
"text": "This is a test message for keystroke pattern analysis",
|
||||
"analysis_type": "general"
|
||||
}
|
||||
|
||||
for i in range(iterations):
|
||||
if i > 0 and i % max(1, iterations // 2) == 0:
|
||||
print(f" Progress: {i}/{iterations}")
|
||||
|
||||
status, latency = self._request("GET", 8001, "/health", None)
|
||||
if status in [200, 503]:
|
||||
self.record("ai_health", latency)
|
||||
|
||||
status, latency = self._request("POST", 8001, "/analyze", payload)
|
||||
if status in [200, 503]:
|
||||
self.record("ai_analyze", latency)
|
||||
|
||||
def benchmark_viz(self, iterations: int = 5):
|
||||
"""Benchmark visualization service."""
|
||||
print("\n=== Visualization Service Benchmark ===")
|
||||
|
||||
chart_types = ["bar", "line", "pie"]
|
||||
|
||||
for i in range(iterations):
|
||||
if i > 0 and i % max(1, iterations // 2) == 0:
|
||||
print(f" Progress: {i}/{iterations}")
|
||||
|
||||
status, latency = self._request("GET", 8002, "/health", None)
|
||||
if status in [200, 503]:
|
||||
self.record("viz_health", latency)
|
||||
|
||||
for chart_type in chart_types:
|
||||
payload = {
|
||||
"title": f"Benchmark {chart_type}",
|
||||
"data": {f"Item{j}": j*100 for j in range(5)},
|
||||
"chart_type": chart_type
|
||||
}
|
||||
|
||||
status, latency = self._request("POST", 8002, "/chart", payload)
|
||||
if status in [200, 503]:
|
||||
self.record(f"viz_chart_{chart_type}", latency)
|
||||
|
||||
def benchmark_throughput(self, duration: int = 10):
|
||||
"""Measure request throughput."""
|
||||
print(f"\n=== Throughput Benchmark ({duration}s) ===")
|
||||
|
||||
endpoints = [
|
||||
(8000, "/health", "api"),
|
||||
(8001, "/health", "ai"),
|
||||
(8002, "/health", "viz"),
|
||||
]
|
||||
|
||||
for port, endpoint, service in endpoints:
|
||||
count = 0
|
||||
start = time.time()
|
||||
|
||||
while time.time() - start < duration:
|
||||
status, _ = self._request("GET", port, endpoint, None)
|
||||
if status in [200, 503]:
|
||||
count += 1
|
||||
|
||||
elapsed = time.time() - start
|
||||
throughput = count / elapsed
|
||||
print(f" {service.upper():3s} Service: {throughput:6.2f} req/s")
|
||||
self.record(f"throughput_{service}", throughput)
|
||||
|
||||
def get_statistics(self, name: str) -> Dict:
|
||||
"""Calculate statistics for benchmark results."""
|
||||
if name not in self.results or len(self.results[name]) == 0:
|
||||
return {}
|
||||
|
||||
values = self.results[name]
|
||||
return {
|
||||
"count": len(values),
|
||||
"min": min(values),
|
||||
"max": max(values),
|
||||
"mean": statistics.mean(values),
|
||||
"median": statistics.median(values),
|
||||
"stdev": statistics.stdev(values) if len(values) > 1 else 0,
|
||||
}
|
||||
|
||||
def print_summary(self):
|
||||
"""Print benchmark summary."""
|
||||
print("\n" + "=" * 70)
|
||||
print("BENCHMARK SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
categories = {
|
||||
"API Service": ["api_health", "api_root", "api_daily_stats", "api_top_words"],
|
||||
"AI Service": ["ai_health", "ai_analyze"],
|
||||
"Visualization": ["viz_health", "viz_chart_bar", "viz_chart_line", "viz_chart_pie"],
|
||||
"Throughput": ["throughput_api", "throughput_ai", "throughput_viz"],
|
||||
}
|
||||
|
||||
for category, metrics in categories.items():
|
||||
print(f"\n{category}:")
|
||||
print("-" * 70)
|
||||
|
||||
for metric in metrics:
|
||||
stats = self.get_statistics(metric)
|
||||
if stats:
|
||||
if "throughput" in metric:
|
||||
print(f" {metric:25s}: {stats['mean']:8.2f} req/s")
|
||||
else:
|
||||
print(f" {metric:25s}: {stats['mean']:8.2f}ms "
|
||||
f"(min: {stats['min']:6.2f}ms, "
|
||||
f"max: {stats['max']:6.2f}ms)")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
|
||||
def generate_report(self, output_file: str = "benchmark_report.json"):
|
||||
"""Generate detailed benchmark report."""
|
||||
report = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"results": {}
|
||||
}
|
||||
|
||||
for name in self.results.keys():
|
||||
report["results"][name] = self.get_statistics(name)
|
||||
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
|
||||
print(f"\nDetailed report saved to: {output_file}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run benchmarks."""
|
||||
print("Tikker Performance Benchmark")
|
||||
print("=" * 70)
|
||||
|
||||
base_url = "http://localhost"
|
||||
if len(sys.argv) > 1:
|
||||
base_url = sys.argv[1]
|
||||
|
||||
runner = BenchmarkRunner(base_url=base_url, verbose=True)
|
||||
|
||||
try:
|
||||
runner.benchmark_api(iterations=10)
|
||||
runner.benchmark_ai(iterations=5)
|
||||
runner.benchmark_viz(iterations=5)
|
||||
runner.benchmark_throughput(duration=10)
|
||||
|
||||
runner.print_summary()
|
||||
runner.generate_report()
|
||||
|
||||
print("\nBenchmark completed successfully!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nBenchmark interrupted by user")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\nBenchmark error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user