-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresults.py
149 lines (127 loc) · 4.47 KB
/
results.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env python3
import sys
import yaml
import os
from tabulate import tabulate
from typing import Dict, List
import json
from dataclasses import dataclass
from datetime import datetime
@dataclass
class FioMetrics:
read_iops: float
read_bw_bytes: float
write_iops: float
write_bw_bytes: float
util_pct: float
runtime_ms: int
@dataclass
class Instance:
id: str
type: str
region: str
metrics: FioMetrics
timestamp: datetime
def load_fio_metrics(fio_json_path: str) -> FioMetrics:
try:
with open(fio_json_path, "r") as f:
content = f.read()
if not content.strip():
raise ValueError(f"File {fio_json_path} is empty")
data = json.loads(content)
except (json.JSONDecodeError, ValueError) as e:
print(f"Error reading {fio_json_path}: {str(e)}")
raise
except Exception as e:
print(f"Unexpected error reading {fio_json_path}: {str(e)}")
raise
job = data["jobs"][0]
return FioMetrics(
read_iops=job["read"]["iops"],
read_bw_bytes=job["read"]["bw_bytes"],
write_iops=job["write"]["iops"],
write_bw_bytes=job["write"]["bw_bytes"],
util_pct=data.get("disk_util", [{"util": 0}])[0]["util"],
runtime_ms=job["job_runtime"]
)
def load_run(run_dir: str) -> List[Instance]:
# Load state file
with open(os.path.join(run_dir, "state.yaml"), "r") as f:
state = yaml.safe_load(f)
instances: List[Instance] = []
# Process each instance directory
for instance in state["instances"]:
instance_dir = os.path.join(run_dir, instance["id"])
fio_json_path = os.path.join(instance_dir, "fio.json")
if not os.path.exists(fio_json_path):
print(f"Warning: No fio.json found for instance {instance['id']}")
continue
metrics = load_fio_metrics(fio_json_path)
instances.append(Instance(
id=instance["id"],
type=instance["type"],
region=instance["region"],
metrics=metrics,
timestamp=datetime.fromtimestamp(os.path.getctime(fio_json_path))
))
return instances
def bytes_to_readable(bytes_per_sec: float) -> str:
for unit in ['B/s', 'KB/s', 'MB/s', 'GB/s']:
if bytes_per_sec < 1024.0:
return f"{bytes_per_sec:,.2f} {unit}"
bytes_per_sec /= 1024.0
return f"{bytes_per_sec:,.2f} TB/s"
def print_results(instances: List[Instance]):
headers = [
"Instance Type",
"Region",
"Read IOPS",
"Write IOPS",
"Total IOPS",
"Read BW",
"Write BW",
"Disk Util %",
"Runtime"
]
rows = []
# Group by instance type
by_type: Dict[str, List[Instance]] = {}
for instance in instances:
if instance.type not in by_type:
by_type[instance.type] = []
by_type[instance.type].append(instance)
# Calculate averages and create rows
for type_name, type_instances in by_type.items():
avg_read_iops = sum(i.metrics.read_iops for i in type_instances) / len(type_instances)
avg_write_iops = sum(i.metrics.write_iops for i in type_instances) / len(type_instances)
avg_read_bw = sum(i.metrics.read_bw_bytes for i in type_instances) / len(type_instances)
avg_write_bw = sum(i.metrics.write_bw_bytes for i in type_instances) / len(type_instances)
avg_util = sum(i.metrics.util_pct for i in type_instances) / len(type_instances)
avg_runtime = sum(i.metrics.runtime_ms for i in type_instances) / len(type_instances)
rows.append([
type_name,
type_instances[0].region,
f"{avg_read_iops:,.2f}",
f"{avg_write_iops:,.2f}",
f"{avg_read_iops + avg_write_iops:,.2f}",
bytes_to_readable(avg_read_bw),
bytes_to_readable(avg_write_bw),
f"{avg_util:.2f}%",
f"{avg_runtime/1000:.1f}s"
])
# Sort by total IOPS
rows.sort(key=lambda x: float(x[4].replace(",", "")), reverse=True)
# Print table
print(tabulate(rows, headers=headers, tablefmt="grid"))
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <run_directory>")
sys.exit(1)
run_dir = sys.argv[1]
if not os.path.isdir(run_dir):
print(f"Error: {run_dir} is not a directory")
sys.exit(1)
instances = load_run(run_dir)
print_results(instances)
if __name__ == "__main__":
main()