mirror of
https://github.com/radixark/miles.git
synced 2026-10-31 07:43:15 +08:00
Co-authored-by: maocheng23 <35615230+maocheng23@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
169 lines
5.4 KiB
Python
169 lines
5.4 KiB
Python
# CPU Memory Profiler Visualization
|
|
#
|
|
# CLI usage:
|
|
# python -m tools.visualize.cpu_memory_profiler_visualize visualize profile.csv
|
|
# python -m tools.visualize.cpu_memory_profiler_visualize visualize profile.csv -o chart.png --title "My Run"
|
|
# python -m tools.visualize.cpu_memory_profiler_visualize visualize-multiple a.csv b.csv -o compare.png
|
|
|
|
import logging
|
|
|
|
import typer
|
|
|
|
logger = logging.getLogger(__name__)
|
|
app = typer.Typer()
|
|
app.callback()(lambda: None)
|
|
|
|
|
|
@app.command()
|
|
def visualize(csv_path: str, output_path: str = None, title: str = None, show: bool = False):
|
|
"""Visualize a CPU memory profile CSV as a timeline chart with phase markers."""
|
|
import csv as csv_mod
|
|
from pathlib import Path
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
csv_path = Path(csv_path)
|
|
output_path = Path(output_path) if output_path else csv_path.with_suffix(".png")
|
|
title = title or f"CPU Memory Profile — {csv_path.name}"
|
|
|
|
# Parse CSV
|
|
times, used, markers = [], [], []
|
|
with open(csv_path) as f:
|
|
for row in csv_mod.DictReader(f):
|
|
t = float(row["time_s"])
|
|
u = float(row["used_gb"])
|
|
m = row["marker"].strip()
|
|
times.append(t)
|
|
used.append(u)
|
|
if m:
|
|
markers.append((t, u, m))
|
|
|
|
# Phase colors
|
|
phase_colors = {
|
|
"generate": "#16a34a",
|
|
"train": "#dc2626",
|
|
"offload_train": "#f59e0b",
|
|
"offload_rollout": "#8b5cf6",
|
|
"onload_weights": "#0891b2",
|
|
"onload_kv": "#06b6d4",
|
|
"update_weights": "#ec4899",
|
|
}
|
|
|
|
def get_color(label):
|
|
phase = label.split("/", 1)[-1] if "/" in label else label
|
|
base = phase.rsplit("_start", 1)[0].rsplit("_end", 1)[0]
|
|
return phase_colors.get(base, "#6b7280")
|
|
|
|
# Plot
|
|
fig, ax = plt.subplots(figsize=(max(16, len(times) * 0.02), 7))
|
|
ax.plot(times, used, linewidth=1.0, color="#2563eb", zorder=2)
|
|
ax.fill_between(times, used, alpha=0.15, color="#2563eb", zorder=1)
|
|
|
|
for t, u, label in markers:
|
|
color = get_color(label)
|
|
ax.axvline(x=t, color=color, linestyle="--", alpha=0.5, linewidth=0.8, zorder=3)
|
|
ax.plot(t, u, "o", color=color, markersize=5, zorder=4)
|
|
|
|
if markers:
|
|
y_min, y_max = min(used), max(used)
|
|
y_range = y_max - y_min
|
|
ax.set_ylim(y_min - y_range * 0.05, y_max + y_range * 0.45)
|
|
n_levels = 4
|
|
for i, (t, u, label) in enumerate(markers):
|
|
color = get_color(label)
|
|
level = i % n_levels
|
|
y_pos = y_max + y_range * (0.08 + level * 0.09)
|
|
ax.annotate(
|
|
label,
|
|
xy=(t, u),
|
|
xytext=(t + (times[-1] - times[0]) * 0.005, y_pos),
|
|
fontsize=7,
|
|
color=color,
|
|
fontweight="bold",
|
|
rotation=45,
|
|
ha="left",
|
|
va="bottom",
|
|
arrowprops=dict(arrowstyle="-", color=color, alpha=0.4, linewidth=0.6),
|
|
)
|
|
|
|
ax.set_xlabel("Time (s)", fontsize=11)
|
|
ax.set_ylabel("CPU Memory Used (GB)", fontsize=11)
|
|
ax.set_title(title, fontsize=13)
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
peak_idx = used.index(max(used))
|
|
ax.annotate(
|
|
f"Peak: {used[peak_idx]:.1f} GB",
|
|
xy=(times[peak_idx], used[peak_idx]),
|
|
xytext=(times[peak_idx] + (times[-1] - times[0]) * 0.03, used[peak_idx]),
|
|
fontsize=9,
|
|
fontweight="bold",
|
|
color="#dc2626",
|
|
arrowprops=dict(arrowstyle="->", color="#dc2626"),
|
|
)
|
|
|
|
plt.tight_layout()
|
|
fig.savefig(output_path, dpi=150, bbox_inches="tight")
|
|
logger.info(f"CPU memory profile chart saved to {output_path}")
|
|
|
|
if show:
|
|
plt.show()
|
|
plt.close(fig)
|
|
|
|
return output_path
|
|
|
|
|
|
@app.command()
|
|
def visualize_multiple(
|
|
csv_paths: list[str],
|
|
output_path: str = typer.Option(None, "-o", "--output-path"),
|
|
title: str = typer.Option(None, "--title"),
|
|
show: bool = typer.Option(False, "--show"),
|
|
):
|
|
"""Overlay multiple CPU memory profiles on one chart with different colors."""
|
|
import csv as csv_mod
|
|
from pathlib import Path
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
line_colors = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b", "#8b5cf6", "#ec4899", "#0891b2"]
|
|
|
|
traces = []
|
|
for p in csv_paths:
|
|
times, used = [], []
|
|
with open(p) as f:
|
|
for row in csv_mod.DictReader(f):
|
|
times.append(float(row["time_s"]))
|
|
used.append(float(row["used_gb"]))
|
|
traces.append((times, used))
|
|
|
|
out = Path(output_path) if output_path else Path(csv_paths[0]).with_name("compare.png")
|
|
title = title or "CPU Memory Profile Comparison"
|
|
|
|
fig, ax = plt.subplots(figsize=(max(16, max(len(t) for t, _ in traces) * 0.02), 7))
|
|
for i, (times, used) in enumerate(traces):
|
|
color = line_colors[i % len(line_colors)]
|
|
label = Path(csv_paths[i]).stem
|
|
ax.plot(times, used, linewidth=1.0, color=color, zorder=2, label=label)
|
|
ax.fill_between(times, used, alpha=0.10, color=color, zorder=1)
|
|
|
|
ax.set_xlabel("Time (s)", fontsize=11)
|
|
ax.set_ylabel("CPU Memory Used (GB)", fontsize=11)
|
|
ax.set_title(title, fontsize=13)
|
|
ax.grid(True, alpha=0.3)
|
|
ax.legend(fontsize=10)
|
|
|
|
plt.tight_layout()
|
|
fig.savefig(out, dpi=150, bbox_inches="tight")
|
|
logger.info(f"Comparison chart saved to {out}")
|
|
|
|
if show:
|
|
plt.show()
|
|
plt.close(fig)
|
|
|
|
return out
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app()
|