288 lines
6.6 KiB
Python
288 lines
6.6 KiB
Python
import math
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
OUT_DIR = "out"
|
|
os.makedirs(OUT_DIR, exist_ok=True)
|
|
|
|
LOG_RE = re.compile(
|
|
r"(PROC_START|PROC_END)\s+pid=(\d+)\s+ppid=(\d+)\s+depth=(\d+)\s+l=(\d+)\s+r=(\d+)\s+ts=([\d.]+)"
|
|
)
|
|
|
|
|
|
class Proc:
|
|
def __init__(self, pid, ppid, depth):
|
|
self.pid = pid
|
|
self.ppid = ppid
|
|
self.depth = depth
|
|
self.start = None
|
|
self.end = None
|
|
self.children = []
|
|
|
|
@property
|
|
def duration(self):
|
|
if self.start is None or self.end is None:
|
|
return 0
|
|
return self.end - self.start
|
|
|
|
|
|
def parse_log(path):
|
|
procs = {}
|
|
|
|
for line in open(path):
|
|
m = LOG_RE.search(line)
|
|
if not m:
|
|
continue
|
|
|
|
typ, pid, ppid, depth, l, r, ts = m.groups()
|
|
pid, ppid, depth, ts = int(pid), int(ppid), int(depth), float(ts)
|
|
|
|
if pid not in procs:
|
|
procs[pid] = Proc(pid, ppid, depth)
|
|
|
|
if typ == "PROC_START":
|
|
procs[pid].start = ts
|
|
else:
|
|
procs[pid].end = ts
|
|
|
|
return procs
|
|
|
|
|
|
def build_tree(procs):
|
|
root = None
|
|
|
|
for p in procs.values():
|
|
if p.ppid in procs:
|
|
procs[p.ppid].children.append(p)
|
|
else:
|
|
root = p
|
|
|
|
return root
|
|
|
|
|
|
def dfs_order(root):
|
|
order = []
|
|
|
|
def dfs(p):
|
|
order.append(p)
|
|
for c in sorted(p.children, key=lambda x: x.start):
|
|
dfs(c)
|
|
|
|
dfs(root)
|
|
return order
|
|
|
|
|
|
def write_md(procs, ordered):
|
|
out_path = os.path.join(OUT_DIR, "processes.md")
|
|
first = min(p.start for p in procs.values())
|
|
|
|
with open(out_path, "w") as f:
|
|
f.write("| PID | PPID | Depth | Start | End | Offset | Duration |\n")
|
|
f.write("|---|---|---|---|---|---|---|\n")
|
|
|
|
for p in ordered:
|
|
f.write(
|
|
f"| {p.pid} | {p.ppid} | {p.depth} | "
|
|
f"{p.start:.6f} | {p.end:.6f} | "
|
|
f"{p.start - first:.6f} | {p.duration:.6f} |\n"
|
|
)
|
|
|
|
|
|
def draw_gantt(ordered):
|
|
out_tree = os.path.join(OUT_DIR, "gantt_tree.png")
|
|
out_pid = os.path.join(OUT_DIR, "gantt_pid.png")
|
|
|
|
first = min(p.start for p in ordered)
|
|
|
|
cmap = plt.get_cmap("tab20")
|
|
color_map = {p.pid: cmap(i % 20) for i, p in enumerate(ordered)}
|
|
|
|
# -------------------------
|
|
# 1) TREE ORDER
|
|
# -------------------------
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 6))
|
|
|
|
for i, p in enumerate(ordered):
|
|
ax.barh(
|
|
i,
|
|
p.duration,
|
|
left=p.start - first,
|
|
height=0.6,
|
|
color=color_map[p.pid],
|
|
)
|
|
ax.text(
|
|
p.start - first,
|
|
i,
|
|
f"PID {p.pid} ({p.depth})",
|
|
va="center",
|
|
)
|
|
|
|
ax.set_yticks(range(len(ordered)))
|
|
ax.set_yticklabels([f"{p.pid} ({p.depth})" for p in ordered])
|
|
ax.set_xlabel("Секунд с начала первого процесса")
|
|
ax.set_title("Диаграмма процессов (в виде дерева)")
|
|
ax.invert_yaxis()
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(out_tree)
|
|
plt.close()
|
|
|
|
# -------------------------
|
|
# 2) PID ORDER
|
|
# -------------------------
|
|
|
|
pid_order = sorted(ordered, key=lambda p: p.pid)
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 6))
|
|
|
|
for i, p in enumerate(pid_order):
|
|
ax.barh(
|
|
i,
|
|
p.duration,
|
|
left=p.start - first,
|
|
height=0.6,
|
|
color=color_map[p.pid],
|
|
)
|
|
ax.text(
|
|
p.start - first,
|
|
i,
|
|
f"PID {p.pid} ({p.depth})",
|
|
va="center",
|
|
)
|
|
|
|
ax.set_yticks(range(len(pid_order)))
|
|
ax.set_yticklabels([f"{p.pid} ({p.depth})" for p in pid_order])
|
|
ax.set_xlabel("Секунд с начала первого процесса")
|
|
ax.set_title("Диаграмма процессов (в порядке PID)")
|
|
ax.invert_yaxis()
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(out_pid)
|
|
plt.close()
|
|
|
|
|
|
# ============================================================
|
|
# PERFORMANCE GRAPH
|
|
# ============================================================
|
|
|
|
|
|
def depth_for_process_count(proc_count):
|
|
"""
|
|
Your C++ program creates processes by recursion depth.
|
|
|
|
depth=0 -> 1 process
|
|
depth=1 -> 3 processes
|
|
depth=2 -> 7 processes
|
|
depth=3 -> 15 processes
|
|
depth=4 -> 31 processes
|
|
depth=5 -> 63 processes
|
|
|
|
This function chooses the closest depth for requested process count.
|
|
"""
|
|
|
|
if proc_count <= 1:
|
|
return 0
|
|
|
|
return max(0, math.ceil(math.log2(proc_count + 1)) - 1)
|
|
|
|
|
|
def run_process_test(n, proc_count):
|
|
max_depth = depth_for_process_count(proc_count)
|
|
|
|
cmd = ["./lab1", str(n), str(max_depth)]
|
|
|
|
start = time.perf_counter()
|
|
|
|
subprocess.run(
|
|
cmd,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
check=False,
|
|
)
|
|
|
|
end = time.perf_counter()
|
|
|
|
return end - start
|
|
|
|
|
|
def draw_performance_graph():
|
|
n = 10000
|
|
proc_counts = list(range(1, 33))
|
|
|
|
times = []
|
|
|
|
print("\nBenchmarking process count performance:")
|
|
|
|
for p in proc_counts:
|
|
t = run_process_test(n, p)
|
|
times.append(t)
|
|
print(f"Processes: {p:2d} | Time: {t:.6f} sec")
|
|
|
|
base_time = times[0]
|
|
|
|
speedup = [base_time / t if t > 0 else 0 for t in times]
|
|
|
|
out_path = os.path.join(OUT_DIR, "process_performance.png")
|
|
|
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
|
|
|
|
# -------------------------
|
|
# TIME GRAPH
|
|
# -------------------------
|
|
|
|
ax1.plot(proc_counts, times, "o-")
|
|
ax1.set_xlabel("Количество процессов")
|
|
ax1.set_ylabel("Время (сек)")
|
|
ax1.set_title("Время выполнения")
|
|
ax1.grid(True)
|
|
|
|
# -------------------------
|
|
# SPEEDUP GRAPH
|
|
# -------------------------
|
|
|
|
ax2.plot(proc_counts, speedup, "s-", label="Реальное ускорение")
|
|
ax2.plot(
|
|
proc_counts,
|
|
proc_counts,
|
|
"--",
|
|
color="black",
|
|
alpha=0.3,
|
|
label="Идеал",
|
|
)
|
|
|
|
ax2.set_xlabel("Количество процессов")
|
|
ax2.set_ylabel("Ускорение")
|
|
ax2.set_title("Масштабируемость")
|
|
ax2.legend()
|
|
ax2.grid(True)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(out_path)
|
|
plt.close()
|
|
|
|
print(f"\nPerformance graph saved to: {out_path}")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) > 1:
|
|
procs = parse_log(sys.argv[1])
|
|
root = build_tree(procs)
|
|
ordered = dfs_order(root)
|
|
|
|
write_md(procs, ordered)
|
|
draw_gantt(ordered)
|
|
|
|
draw_performance_graph()
|
|
|
|
print(f"\nOutputs saved to: {OUT_DIR}/")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|