- Add database migrations (006-028) for face recognition, identity, file_uuid - Add test scripts for ASR, face, search, processing - Add portal frontend (Tauri) - Add config, benchmark, and monitoring utilities - Add model checkpoints and pretrained model references
162 lines
5.1 KiB
Python
162 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Benchmark ASR processor direct vs chunked transcription overhead."""
|
|
|
|
import sys
|
|
import os
|
|
import subprocess
|
|
import json
|
|
import tempfile
|
|
import time
|
|
import shutil
|
|
import statistics
|
|
|
|
# Use a small video clip for consistent benchmarking
|
|
VIDEO_SOURCE = "../test_video/BigBuckBunny_320x180.mp4" # 10 minutes, 62MB
|
|
if not os.path.exists(VIDEO_SOURCE):
|
|
print(f"Video not found: {VIDEO_SOURCE}")
|
|
sys.exit(1)
|
|
|
|
# Create temporary directory for all test runs
|
|
temp_dir = tempfile.mkdtemp(prefix="asr_bench_")
|
|
print(f"Benchmark directory: {temp_dir}")
|
|
|
|
|
|
def run_asr_mode(mode_name, max_direct_duration, chunk_duration=600):
|
|
"""Run ASR processor with given parameters, return timing and resource stats."""
|
|
clip_path = os.path.join(temp_dir, f"clip_{mode_name}.mp4")
|
|
output_path = os.path.join(temp_dir, f"output_{mode_name}.json")
|
|
|
|
# Copy source video to clip path (no transcoding)
|
|
shutil.copy2(VIDEO_SOURCE, clip_path)
|
|
|
|
env = os.environ.copy()
|
|
env["MOMENTRY_ASR_MAX_DIRECT_DURATION"] = str(max_direct_duration)
|
|
env["MOMENTRY_ASR_CHUNK_DURATION"] = str(chunk_duration)
|
|
env["MOMENTRY_ASR_MODEL_SIZE"] = "tiny"
|
|
env["MOMENTRY_ASR_COMPUTE_TYPE"] = "int8"
|
|
|
|
cmd = [
|
|
"/opt/homebrew/bin/python3.11",
|
|
"scripts/asr_processor.py",
|
|
clip_path,
|
|
output_path,
|
|
"--uuid",
|
|
f"bench_{mode_name}",
|
|
]
|
|
|
|
# Start monitoring (external)
|
|
import psutil
|
|
|
|
start_time = time.time()
|
|
proc = subprocess.Popen(
|
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
|
|
)
|
|
|
|
# Monitor CPU and memory of child process
|
|
cpu_percents = []
|
|
memory_mbs = []
|
|
|
|
while True:
|
|
try:
|
|
p = psutil.Process(proc.pid)
|
|
cpu = p.cpu_percent(interval=0.1)
|
|
mem = p.memory_info().rss / (1024 * 1024)
|
|
cpu_percents.append(cpu)
|
|
memory_mbs.append(mem)
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
break
|
|
if proc.poll() is not None:
|
|
# Process ended, wait a bit for final stats
|
|
time.sleep(0.1)
|
|
break
|
|
|
|
stdout, stderr = proc.communicate(timeout=1)
|
|
elapsed = time.time() - start_time
|
|
returncode = proc.returncode
|
|
|
|
# Read output
|
|
segments = []
|
|
if os.path.exists(output_path):
|
|
with open(output_path, "r") as f:
|
|
data = json.load(f)
|
|
segments = data.get("segments", [])
|
|
|
|
# Clean up temporary files
|
|
try:
|
|
os.unlink(clip_path)
|
|
os.unlink(output_path)
|
|
except:
|
|
pass
|
|
|
|
return {
|
|
"mode": mode_name,
|
|
"elapsed": elapsed,
|
|
"returncode": returncode,
|
|
"segments": len(segments),
|
|
"cpu_avg": statistics.mean(cpu_percents) if cpu_percents else 0,
|
|
"cpu_max": max(cpu_percents) if cpu_percents else 0,
|
|
"memory_avg": statistics.mean(memory_mbs) if memory_mbs else 0,
|
|
"memory_max": max(memory_mbs) if memory_mbs else 0,
|
|
"stderr": stderr.decode() if stderr else "",
|
|
}
|
|
|
|
|
|
try:
|
|
# Run direct transcription (clip duration ~600s, max_direct=1800)
|
|
print("Running direct transcription benchmark...")
|
|
direct = run_asr_mode("direct", max_direct_duration=1800, chunk_duration=600)
|
|
|
|
# Run chunked transcription (force chunked with max_direct=300, chunk=120)
|
|
print("Running chunked transcription benchmark...")
|
|
chunked = run_asr_mode("chunked", max_direct_duration=300, chunk_duration=120)
|
|
|
|
# Calculate overhead
|
|
overhead = (chunked["elapsed"] - direct["elapsed"]) / direct["elapsed"] * 100
|
|
|
|
# Print results
|
|
print("\n" + "=" * 60)
|
|
print("ASR PROCESSOR BENCHMARK RESULTS")
|
|
print("=" * 60)
|
|
print(f"Test video: {VIDEO_SOURCE}")
|
|
print(f"Video duration: ~10 minutes (600 seconds)")
|
|
print()
|
|
print("Direct Transcription:")
|
|
print(f" Time: {direct['elapsed']:.1f}s")
|
|
print(f" Segments: {direct['segments']}")
|
|
print(f" CPU avg/max: {direct['cpu_avg']:.1f}% / {direct['cpu_max']:.1f}%")
|
|
print(
|
|
f" Memory avg/max: {direct['memory_avg']:.1f} MB / {direct['memory_max']:.1f} MB"
|
|
)
|
|
print()
|
|
print("Chunked Transcription:")
|
|
print(f" Time: {chunked['elapsed']:.1f}s")
|
|
print(f" Segments: {chunked['segments']}")
|
|
print(f" CPU avg/max: {chunked['cpu_avg']:.1f}% / {chunked['cpu_max']:.1f}%")
|
|
print(
|
|
f" Memory avg/max: {chunked['memory_avg']:.1f} MB / {chunked['memory_max']:.1f} MB"
|
|
)
|
|
print()
|
|
print("OVERHEAD ANALYSIS:")
|
|
print(f" Time overhead: {overhead:.2f}%")
|
|
if overhead <= 5:
|
|
print(f" ✅ PASS: Overhead ≤5% requirement")
|
|
else:
|
|
print(f" ❌ FAIL: Overhead exceeds 5% limit")
|
|
print()
|
|
|
|
# Check for errors
|
|
if direct["returncode"] != 0:
|
|
print(f"WARNING: Direct transcription returned {direct['returncode']}")
|
|
if chunked["returncode"] != 0:
|
|
print(f"WARNING: Chunked transcription returned {chunked['returncode']}")
|
|
|
|
except Exception as e:
|
|
print(f"Benchmark failed: {e}")
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
finally:
|
|
# Clean up directory
|
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
print(f"Cleaned up {temp_dir}")
|