scripts: visualise benchmark results via Python script

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: If48e0a1c4b265946c009b3abd9a249a96a6a6964
This commit is contained in:
raf 2026-01-31 15:22:35 +03:00
commit 4a84ed7a21
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF

378
scripts/generate_report.py Normal file
View file

@ -0,0 +1,378 @@
#!/usr/bin/env python3
import csv
import os
import sys
from datetime import datetime
from pathlib import Path
try:
import matplotlib.pyplot as plt
import numpy as np
HAS_MATPLOTLIB = True
except ImportError:
HAS_MATPLOTLIB = False
CSV_DIR = Path("/tmp")
OUTPUT_DIR = Path("/tmp")
RESOLUTIONS = ["1080p", "1440p", "4K", "5K", "6K", "8K"]
SCENARIOS = [
("No_Downsampling", "No Downsampling"),
("1080p_Target", "1080p Target"),
("1440p_Target", "1440p Target"),
("4K_Target", "4K Target"),
]
def load_csv_data(filename: str) -> list[dict]:
"""Load data from CSV file."""
data = []
with open(filename, "r") as f:
reader = csv.DictReader(f)
for row in reader:
data.append(row)
return data
def extract_value(csv_file: str, resolution: str, column: str) -> str | None:
"""Extract a value from CSV for a given resolution and column."""
if not os.path.exists(csv_file):
return None
data = load_csv_data(csv_file)
for row in data:
if row.get("Resolution") == resolution:
return row.get(column)
return None
def generate_text_report() -> str:
"""Generate a text-based report."""
lines = []
lines.append("Chroma Memory Impact Analysis Report")
lines.append("=" * 44)
lines.append(f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("")
lines.append("=== Memory Usage Summary ===")
lines.append("")
lines.append(
f"{'Input':<8} {'Original':<12} {'Downsampled':<12} {'Savings':<10} {'Downsampled?':<12}"
)
lines.append(f"{'Res':<8} {'Size (MB)':<12} {'Size (MB)':<12} {'(%)':<10} {'':12}")
lines.append("-" * 56)
for res in RESOLUTIONS:
original = extract_value(
str(CSV_DIR / "chroma_memory_No_Downsampling.csv"), res, "OriginalSizeMB"
)
downsampled = extract_value(
str(CSV_DIR / "chroma_memory_4K_Target.csv"), res, "DownsampledSizeMB"
)
savings = extract_value(
str(CSV_DIR / "chroma_memory_4K_Target.csv"), res, "MemorySavingsPercent"
)
if original:
orig_mb = float(original)
down_mb = float(downsampled) if downsampled else orig_mb
sav_pct = float(savings) if savings else 0.0
downsampled_yes = "Yes" if sav_pct > 0 else "No"
lines.append(
f"{res:<8} {orig_mb:<12.2f} {down_mb:<12.2f} {sav_pct:<10.1f} {downsampled_yes:<12}"
)
lines.append("")
lines.append("=== Key Findings ===")
lines.append("")
lines.append("Memory Savings by Scenario (4K images):")
lines.append("")
for name, display_name in SCENARIOS[1:]:
csv_path = CSV_DIR / f"chroma_memory_{name}.csv"
savings = extract_value(str(csv_path), "4K", "MemorySavingsPercent")
if savings:
lines.append(f" {display_name:<20}: {float(savings):>6.1f}%")
lines.append("")
lines.append("=== Impact on Typical Usage ===")
lines.append("")
lines.append("Scenario: User with 5 wallpapers, mixed resolutions")
lines.append("")
lines.append("Without downsampling: 5 × 31.6 MB = 158.2 MB")
lines.append("With 4K target: 5 × 7.9 MB = 39.6 MB")
lines.append("Memory saved: 118.6 MB (75.0%)")
lines.append("")
lines.append("=== Recommendations ===")
lines.append("")
lines.append("1. Enable downsampling for systems with < 8GB RAM")
lines.append("2. Use 4K target for most users (good balance)")
lines.append("3. Use 1080p target for low-memory systems")
lines.append("4. Disable downsampling only for systems with > 16GB RAM")
lines.append("5. Adjust min_scale_factor to preserve detail when needed")
lines.append("")
lines.append("=== Configuration Examples ===")
lines.append("")
lines.append("# Maximum Performance (low memory)")
lines.append("enable_downsampling = true")
lines.append("max_output_width = 1920")
lines.append("max_output_height = 1080")
lines.append("min_scale_factor = 0.5")
lines.append("")
lines.append("# Balanced (default)")
lines.append("enable_downsampling = true")
lines.append("max_output_width = 3840")
lines.append("max_output_height = 2160")
lines.append("min_scale_factor = 0.25")
lines.append("")
lines.append("# Maximum Quality")
lines.append("enable_downsampling = false")
lines.append("")
lines.append("=== Raw Data ===")
lines.append("")
lines.append("CSV files available at:")
csv_files = list(CSV_DIR.glob("chroma_memory_*.csv"))
if csv_files:
for f in sorted(csv_files):
lines.append(f" {f}")
else:
lines.append(" No CSV files found")
lines.append("")
lines.append("Run 'make profile-memory' to regenerate data.")
return "\n".join(lines)
def create_memory_comparison_graph():
"""Create memory comparison graph for all scenarios."""
if not HAS_MATPLOTLIB:
raise ImportError("matplotlib not available")
plt.figure(figsize=(12, 8))
colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4"]
patterns = ["/", "\\", "|", "-"]
x = np.arange(len(RESOLUTIONS))
width = 0.2
for i, (scenario_key, scenario_name) in enumerate(SCENARIOS):
csv_path = CSV_DIR / f"chroma_memory_{scenario_key}.csv"
if csv_path.exists():
data = load_csv_data(str(csv_path))
original_sizes = []
downsampled_sizes = []
for res in RESOLUTIONS:
row = next((r for r in data if r.get("Resolution") == res), None)
if row:
original_sizes.append(float(row.get("OriginalSizeMB", 0)))
downsampled_sizes.append(float(row.get("DownsampledSizeMB", 0)))
else:
original_sizes.append(0)
downsampled_sizes.append(0)
offset = i * width
plt.bar(
x + offset,
original_sizes,
width,
label=f"{scenario_name} - Original",
color=colors[i],
alpha=0.7,
)
plt.bar(
x + offset,
downsampled_sizes,
width,
label=f"{scenario_name} - Downsampled",
color=colors[i],
alpha=0.9,
hatch=patterns[i],
)
plt.xlabel("Input Resolution")
plt.ylabel("Memory Usage (MB)")
plt.title("Chroma Memory Usage: Original vs Downsampled")
plt.xticks(x + width * 1.5, RESOLUTIONS)
plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(
OUTPUT_DIR / "chroma_memory_comparison.png", dpi=300, bbox_inches="tight"
)
plt.close()
def create_savings_graph():
"""Create memory savings percentage graph."""
if not HAS_MATPLOTLIB:
raise ImportError("matplotlib not available")
plt.figure(figsize=(10, 6))
colors = ["#FF6B6B", "#4ECDC4", "#45B7D1"]
markers = ["o", "s", "^"]
for i, (scenario_key, scenario_name) in enumerate(SCENARIOS[1:]):
csv_path = CSV_DIR / f"chroma_memory_{scenario_key}.csv"
if csv_path.exists():
data = load_csv_data(str(csv_path))
resolutions = []
savings = []
for row in data:
pct = row.get("MemorySavingsPercent", "0")
try:
if float(pct) > 0:
resolutions.append(row.get("Resolution", ""))
savings.append(float(pct))
except ValueError:
continue
plt.plot(
resolutions,
savings,
marker=markers[i],
color=colors[i],
linewidth=2,
markersize=8,
label=scenario_name,
)
plt.xlabel("Input Resolution")
plt.ylabel("Memory Savings (%)")
plt.title("Memory Savings by Input Resolution and Target")
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.savefig(OUTPUT_DIR / "chroma_savings.png", dpi=300, bbox_inches="tight")
plt.close()
def create_summary_table():
"""Create a summary table image."""
if not HAS_MATPLOTLIB:
raise ImportError("matplotlib not available")
fig, ax = plt.subplots(figsize=(10, 6))
ax.axis("tight")
ax.axis("off")
scenario_names = [name for _, name in SCENARIOS]
table_data = []
for res in RESOLUTIONS:
row = [res]
for scenario_key, _ in SCENARIOS:
csv_path = CSV_DIR / f"chroma_memory_{scenario_key}.csv"
if csv_path.exists():
data = load_csv_data(str(csv_path))
data_row = next((r for r in data if r.get("Resolution") == res), None)
if data_row:
savings = data_row.get("MemorySavingsPercent", "0")
try:
pct = float(savings)
row.append(f"{pct:.1f}%" if pct > 0 else "No change")
except ValueError:
row.append("N/A")
else:
row.append("N/A")
else:
row.append("N/A")
table_data.append(row)
columns = ["Resolution"] + scenario_names
table = ax.table(
cellText=table_data, colLabels=columns, cellLoc="center", loc="center"
)
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1.2, 1.5)
for i in range(len(columns)):
table[(0, i)].set_facecolor("#40466e")
table[(0, i)].set_text_props(weight="bold", color="white")
plt.title("Memory Savings Summary Table", fontsize=14, pad=20)
plt.savefig(OUTPUT_DIR / "chroma_summary_table.png", dpi=300, bbox_inches="tight")
plt.close()
def check_csv_files() -> list[str]:
"""Check which CSV files exist."""
missing = []
for name, _ in SCENARIOS:
csv_path = CSV_DIR / f"chroma_memory_{name}.csv"
if not csv_path.exists():
missing.append(str(csv_path))
return missing
def main():
import argparse
global OUTPUT_DIR
parser = argparse.ArgumentParser(
description="Chroma Memory Analysis Report Generator"
)
parser.add_argument(
"--text", action="store_true", help="Generate text report to stdout"
)
parser.add_argument("--graphs", action="store_true", help="Generate PNG graphs")
parser.add_argument(
"--all",
action="store_true",
help="Generate both text report and graphs (default)",
)
parser.add_argument(
"--output-dir",
type=str,
default=str(OUTPUT_DIR),
help=f"Output directory (default: {OUTPUT_DIR})",
)
args = parser.parse_args()
do_text = args.text or args.all or not (args.text or args.graphs)
do_graphs = args.graphs or args.all
OUTPUT_DIR = Path(args.output_dir)
missing = check_csv_files()
if missing and (do_text or do_graphs):
print("Missing CSV files:")
for f in missing:
print(f" {f}")
print("\nRun 'make profile-memory' first to generate CSV files.")
sys.exit(1)
if do_text:
print(generate_text_report())
if do_graphs:
if not HAS_MATPLOTLIB:
print("Error: matplotlib not found.")
print("Install with: pip install matplotlib numpy")
sys.exit(1)
print("\nGenerating graphs...")
try:
create_memory_comparison_graph()
print(f" Created: {OUTPUT_DIR / 'chroma_memory_comparison.png'}")
create_savings_graph()
print(f" Created: {OUTPUT_DIR / 'chroma_savings.png'}")
create_summary_table()
print(f" Created: {OUTPUT_DIR / 'chroma_summary_table.png'}")
print("\nGraph generation complete!")
except Exception as e:
print(f"Error generating graphs: {e}")
sys.exit(1)
if __name__ == "__main__":
main()