|
#!/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()
|