Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,6 @@ dmypy.json

# idea
.idea/
launch.py
.vscode/launch.json
.DS_Store
123 changes: 49 additions & 74 deletions asitop/asitop.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,27 @@ def main():
)

ram_gauge = HGauge(title="RAM Usage", val=0, color=args.color)
ecpu_bw_gauge = HGauge(title="E-CPU B/W", val=50, color=args.color)
pcpu_bw_gauge = HGauge(title="P-CPU B/W", val=50, color=args.color)
gpu_bw_gauge = HGauge(title="GPU B/W", val=50, color=args.color)
media_bw_gauge = HGauge(title="Media B/W", val=50, color=args.color)
bw_gauges = [HSplit(
ecpu_bw_gauge,
pcpu_bw_gauge,
disk_read_chart = HChart(title="Disk Read", color=args.color)
disk_write_chart = HChart(title="Disk Write", color=args.color)
network_download_chart = HChart(title="Network Download", color=args.color)
network_upload_chart = HChart(title="Network Upload", color=args.color)
bw_charts = [HSplit(
disk_read_chart,
disk_write_chart,
),
HSplit(
gpu_bw_gauge,
media_bw_gauge,
network_download_chart,
network_upload_chart,
)] if args.show_cores else [
HSplit(
ecpu_bw_gauge,
pcpu_bw_gauge,
gpu_bw_gauge,
media_bw_gauge,
disk_read_chart,
disk_write_chart,
network_download_chart,
network_upload_chart,
)]
memory_gauges = VSplit(
ram_gauge,
*bw_gauges,
*bw_charts,
border_color=args.color,
title="Memory"
)
Expand Down Expand Up @@ -114,7 +114,7 @@ def main():
)

usage_gauges = ui.items[0]
bw_gauges = memory_gauges.items[1]
bw_charts = memory_gauges.items[1]

cpu_title = "".join([
soc_info_dict["name"],
Expand Down Expand Up @@ -180,7 +180,7 @@ def get_avg(inlist):
count += 1
ready = parse_powermetrics(timecode=timecode)
if ready:
cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, bandwidth_metrics, timestamp = ready
cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, network_metrics_dict, disk_metrics_dict, timestamp = ready

if timestamp > last_timestamp:
last_timestamp = timestamp
Expand Down Expand Up @@ -275,68 +275,43 @@ def get_avg(inlist):
])
ram_gauge.value = ram_metrics_dict["free_percent"]

ecpu_bw_percent = int(
(bandwidth_metrics["ECPU DCS RD"] + bandwidth_metrics[
"ECPU DCS WR"]) / args.interval / max_cpu_bw * 100)
ecpu_read_GB = bandwidth_metrics["ECPU DCS RD"] / \
args.interval
ecpu_write_GB = bandwidth_metrics["ECPU DCS WR"] / \
args.interval
ecpu_bw_gauge.title = "".join([
"E-CPU: ",
'{0:.1f}'.format(ecpu_read_GB + ecpu_write_GB),
"GB/s"
disk_read_rate=disk_metrics_dict["read_rate"]
disk_read_chart.title = "".join([
"Disk Read: ",
'{0:.1f}'.format(disk_read_rate),
"MB/s"
])
ecpu_bw_gauge.value = ecpu_bw_percent

pcpu_bw_percent = int(
(bandwidth_metrics["PCPU DCS RD"] + bandwidth_metrics[
"PCPU DCS WR"]) / args.interval / max_cpu_bw * 100)
pcpu_read_GB = bandwidth_metrics["PCPU DCS RD"] / \
args.interval
pcpu_write_GB = bandwidth_metrics["PCPU DCS WR"] / \
args.interval
pcpu_bw_gauge.title = "".join([
"P-CPU: ",
'{0:.1f}'.format(pcpu_read_GB + pcpu_write_GB),
"GB/s"
disk_read_percent=int(disk_read_rate/soc_info_dict["disk_read_max"]*100)
disk_read_chart.append(disk_read_percent)

disk_write_rate=disk_metrics_dict["write_rate"]
disk_write_chart.title = "".join([
"Disk Write: ",
'{0:.1f}'.format(disk_write_rate),
"MB/s"
])
pcpu_bw_gauge.value = pcpu_bw_percent

gpu_bw_percent = int(
(bandwidth_metrics["GFX DCS RD"] + bandwidth_metrics["GFX DCS WR"]) / max_gpu_bw * 100)
gpu_read_GB = bandwidth_metrics["GFX DCS RD"]
gpu_write_GB = bandwidth_metrics["GFX DCS WR"]
gpu_bw_gauge.title = "".join([
"GPU: ",
'{0:.1f}'.format(gpu_read_GB + gpu_write_GB),
"GB/s"
])
gpu_bw_gauge.value = gpu_bw_percent

media_bw_percent = int(
bandwidth_metrics["MEDIA DCS"] / args.interval / max_media_bw * 100)
media_bw_gauge.title = "".join([
"Media: ",
'{0:.1f}'.format(
bandwidth_metrics["MEDIA DCS"] / args.interval),
"GB/s"
disk_write_percent=int(disk_write_rate/soc_info_dict["disk_write_max"]*100)
disk_write_chart.append(disk_write_percent)

network_download_rate=network_metrics_dict["download_rate"]
network_download_chart.title = "".join([
"Network Download: ",
'{0:.1f}'.format(network_download_rate),
"MB/s"
])
media_bw_gauge.value = media_bw_percent

total_bw_GB = (
bandwidth_metrics["DCS RD"] + bandwidth_metrics["DCS WR"]) / args.interval
bw_gauges.title = "".join([
"Memory Bandwidth: ",
'{0:.2f}'.format(total_bw_GB),
" GB/s (R:",
'{0:.2f}'.format(
bandwidth_metrics["DCS RD"] / args.interval),
"/W:",
'{0:.2f}'.format(
bandwidth_metrics["DCS WR"] / args.interval),
" GB/s)"
network_download_percent=int(network_download_rate/soc_info_dict["max_network_speed"]*100)
network_download_chart.append(network_download_percent)

network_upload_rate=network_metrics_dict["upload_rate"]
network_upload_chart.title = "".join([
"Network Upload: ",
'{0:.1f}'.format(network_upload_rate),
"MB/s"
])
network_upload_percent=int(network_upload_rate/soc_info_dict["max_network_speed"]*100)
network_upload_chart.append(network_upload_percent)

bw_charts.title = "Bandwidths"
if args.show_cores:
bw_gauges_ext = memory_gauges.items[2]
bw_gauges_ext.title = "Memory Bandwidth:"
Expand Down
86 changes: 42 additions & 44 deletions asitop/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ def parse_thermal_pressure(powermetrics_parse):


def parse_bandwidth_metrics(powermetrics_parse):
bandwidth_metrics = powermetrics_parse["bandwidth_counters"]
# bandwidth_metrics = powermetrics_parse["bandwidth_counters"]
bandwidth_metrics_dict = {}
data_fields = ["PCPU0 DCS RD", "PCPU0 DCS WR",
"PCPU1 DCS RD", "PCPU1 DCS WR",
Expand Down Expand Up @@ -31,49 +31,47 @@ def parse_bandwidth_metrics(powermetrics_parse):
"DCS RD", "DCS WR"]
for h in data_fields:
bandwidth_metrics_dict[h] = 0
for l in bandwidth_metrics:
if l["name"] in data_fields:
bandwidth_metrics_dict[l["name"]] = l["value"]/(1e9)
bandwidth_metrics_dict["PCPU DCS RD"] = bandwidth_metrics_dict["PCPU DCS RD"] + \
bandwidth_metrics_dict["PCPU0 DCS RD"] + \
bandwidth_metrics_dict["PCPU1 DCS RD"] + \
bandwidth_metrics_dict["PCPU2 DCS RD"] + \
bandwidth_metrics_dict["PCPU3 DCS RD"]
bandwidth_metrics_dict["PCPU DCS WR"] = bandwidth_metrics_dict["PCPU DCS WR"] + \
bandwidth_metrics_dict["PCPU0 DCS WR"] + \
bandwidth_metrics_dict["PCPU1 DCS WR"] + \
bandwidth_metrics_dict["PCPU2 DCS WR"] + \
bandwidth_metrics_dict["PCPU3 DCS WR"]
bandwidth_metrics_dict["JPG DCS RD"] = bandwidth_metrics_dict["JPG DCS RD"] + \
bandwidth_metrics_dict["JPG0 DCS RD"] + \
bandwidth_metrics_dict["JPG1 DCS RD"] + \
bandwidth_metrics_dict["JPG2 DCS RD"] + \
bandwidth_metrics_dict["JPG3 DCS RD"]
bandwidth_metrics_dict["JPG DCS WR"] = bandwidth_metrics_dict["JPG DCS WR"] + \
bandwidth_metrics_dict["JPG0 DCS WR"] + \
bandwidth_metrics_dict["JPG1 DCS WR"] + \
bandwidth_metrics_dict["JPG2 DCS WR"] + \
bandwidth_metrics_dict["JPG3 DCS WR"]
bandwidth_metrics_dict["VENC DCS RD"] = bandwidth_metrics_dict["VENC DCS RD"] + \
bandwidth_metrics_dict["VENC0 DCS RD"] + \
bandwidth_metrics_dict["VENC1 DCS RD"] + \
bandwidth_metrics_dict["VENC2 DCS RD"] + \
bandwidth_metrics_dict["VENC3 DCS RD"]
bandwidth_metrics_dict["VENC DCS WR"] = bandwidth_metrics_dict["VENC DCS WR"] + \
bandwidth_metrics_dict["VENC0 DCS WR"] + \
bandwidth_metrics_dict["VENC1 DCS WR"] + \
bandwidth_metrics_dict["VENC2 DCS WR"] + \
bandwidth_metrics_dict["VENC3 DCS WR"]
bandwidth_metrics_dict["MEDIA DCS"] = sum([
bandwidth_metrics_dict["ISP DCS RD"], bandwidth_metrics_dict["ISP DCS WR"],
bandwidth_metrics_dict["STRM CODEC DCS RD"], bandwidth_metrics_dict["STRM CODEC DCS WR"],
bandwidth_metrics_dict["PRORES DCS RD"], bandwidth_metrics_dict["PRORES DCS WR"],
bandwidth_metrics_dict["VDEC DCS RD"], bandwidth_metrics_dict["VDEC DCS WR"],
bandwidth_metrics_dict["VENC DCS RD"], bandwidth_metrics_dict["VENC DCS WR"],
bandwidth_metrics_dict["JPG DCS RD"], bandwidth_metrics_dict["JPG DCS WR"],
])
# for l in bandwidth_metrics:
# if l["name"] in data_fields:
# bandwidth_metrics_dict[l["name"]] = l["value"]/(1e9)
bandwidth_metrics_dict["PCPU DCS RD"] = 0
bandwidth_metrics_dict["PCPU DCS WR"] = 0
bandwidth_metrics_dict["JPG DCS RD"] = 0
bandwidth_metrics_dict["JPG DCS WR"] = 0
bandwidth_metrics_dict["VENC DCS RD"] = 0
bandwidth_metrics_dict["VENC DCS WR"] = 0
bandwidth_metrics_dict["MEDIA DCS"] = 0
return bandwidth_metrics_dict

def parse_disk_metrics(powermetrics_parse):
"""Parse disk metrics, returns r/w rate in MByte/s

Args:
powermetrics_parse (dict): The result of plistlib loaded data

Returns:
dict: A dictionary which has write_rate and read_rate key, in MByte/s
"""
disk_metrics=powermetrics_parse["disk"]
return {
"write_rate":disk_metrics["wbytes_per_s"]/(1024*1024),
"read_rate":disk_metrics["rbytes_per_s"]/(1024*1024)
}

def parse_network_metrics(powermetrics_parse):
"""Parse network metrics, returns upload/download rate in MByte/s

Args:
powermetrics_parse (dict): The result of plistlib loaded data

Returns:
dict: A dictionary which has upload_rate and download_rate key, in MByte/s
"""
network_metrics=powermetrics_parse["network"]
return {
"upload_rate":network_metrics["obyte_rate"]/(1024*1024),
"download_rate":network_metrics["ibyte_rate"]/(1024*1024)
}

def parse_cpu_metrics(powermetrics_parse):
e_core = []
Expand Down Expand Up @@ -124,10 +122,10 @@ def parse_cpu_metrics(powermetrics_parse):
cpu_metric_dict["P0-Cluster_freq_Mhz"], cpu_metric_dict["P1-Cluster_freq_Mhz"])
# power
cpu_metric_dict["ane_W"] = cpu_metrics["ane_energy"]/1000
cpu_metric_dict["dram_W"] = cpu_metrics["dram_energy"]/1000
cpu_metric_dict["dram_W"] = 0
cpu_metric_dict["cpu_W"] = cpu_metrics["cpu_energy"]/1000
cpu_metric_dict["gpu_W"] = cpu_metrics["gpu_energy"]/1000
cpu_metric_dict["package_W"] = cpu_metrics["package_energy"]/1000
cpu_metric_dict["package_W"] = cpu_metrics["combined_power"]/1000
Copy link
Copy Markdown

@dvessel dvessel Nov 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combined_power is not a direct replacement for package_energy. This is why I was getting bad “Package power” readings.

cpu/gpu/ane _energy has to be added manually to get the combined_energy.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, according to (command-line invoked) powermetrics, combined_energy IS cpu+gpu+ane.

return cpu_metric_dict


Expand Down
70 changes: 64 additions & 6 deletions asitop/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@ def parse_powermetrics(path='/tmp/asitop_powermetrics', timecode="0"):
thermal_pressure = parse_thermal_pressure(powermetrics_parse)
cpu_metrics_dict = parse_cpu_metrics(powermetrics_parse)
gpu_metrics_dict = parse_gpu_metrics(powermetrics_parse)
bandwidth_metrics = parse_bandwidth_metrics(powermetrics_parse)
# bandwidth_metrics = parse_bandwidth_metrics(powermetrics_parse)
network_metrics_dict = parse_network_metrics(powermetrics_parse)
disk_metrics_dict = parse_disk_metrics(powermetrics_parse)
timestamp = powermetrics_parse["timestamp"]
return cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, bandwidth_metrics, timestamp
return cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, network_metrics_dict, disk_metrics_dict, timestamp
except Exception as e:
if data:
if len(data) > 1:
powermetrics_parse = plistlib.loads(data[-2])
thermal_pressure = parse_thermal_pressure(powermetrics_parse)
cpu_metrics_dict = parse_cpu_metrics(powermetrics_parse)
gpu_metrics_dict = parse_gpu_metrics(powermetrics_parse)
bandwidth_metrics = parse_bandwidth_metrics(powermetrics_parse)
# bandwidth_metrics = parse_bandwidth_metrics(powermetrics_parse)
network_metrics_dict = parse_network_metrics(powermetrics_parse)
disk_metrics_dict = parse_disk_metrics(powermetrics_parse)
timestamp = powermetrics_parse["timestamp"]
return cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, bandwidth_metrics, timestamp
return cpu_metrics_dict, gpu_metrics_dict, thermal_pressure, network_metrics_dict, disk_metrics_dict, timestamp
return False


Expand All @@ -46,13 +50,13 @@ def run_powermetrics_process(timecode, nice=10, interval=1000):
#ver, *_ = platform.mac_ver()
#major_ver = int(ver.split(".")[0])
for tmpf in glob.glob("/tmp/asitop_powermetrics*"):
os.remove(tmpf)
subprocess.Popen(["sudo","rm",tmpf])
output_file_flag = "-o"
command = " ".join([
"sudo nice -n",
str(nice),
"powermetrics",
"--samplers cpu_power,gpu_power,thermal,bandwidth",
"--samplers cpu_power,gpu_power,thermal,disk,network",
output_file_flag,
"/tmp/asitop_powermetrics"+timecode,
"-f plist",
Expand Down Expand Up @@ -101,6 +105,17 @@ def get_cpu_info():
cpu_info_dict[h] = value
return cpu_info_dict

def get_disk_info():
"""Returns disk capacity in GB
"""
total,used,free,percent=psutil.disk_usage('/')
total_GB=total/(1024**3)
possible_disk_capacities=[256,512,1024,2048,4096,8192]
#256G might be 240~ G here, so we do this
for p in possible_disk_capacities:
if p>total_GB:
return p
#TODO: However, it cannot handle things correctly all the time if the disk is partitioned, for example, installed Asahi Linux.

def get_core_counts():
cores_info = os.popen('sysctl -a | grep hw.perflevel').read()
Expand Down Expand Up @@ -145,6 +160,8 @@ def get_soc_info():
"p_core_count": p_core_count,
"gpu_core_count": get_gpu_cores()
}
#Theorically Thunderbolt4 can reach 5120 MByte/s, but who will ever use an Ethernet adaptor like that?
soc_info["max_network_speed"]=128
# TDP (power)
if soc_info["name"] == "Apple M1 Max":
soc_info["cpu_max_power"] = 30
Expand All @@ -165,6 +182,47 @@ def get_soc_info():
soc_info["cpu_max_power"] = 20
soc_info["gpu_max_power"] = 20
# bandwidth
disk_capacity=get_disk_info()
if soc_info["name"] in ["Apple M1 Max","Apple M1 Pro","Apple M1","Apple M1 Ultra"]:
#According to https://forums.macrumors.com/threads/mbp-2021-ssd-speed-comparison-please-contribute.2320899/
#In MByte/s
write_speeds={
256:2500,
512:5000,
1024:6000,
2048:6500,
4096:7500,
8192:7500,
}
read_speeds={
256:3000,
512:5500,
1024:5500,
2048:5500,
4096:6000,
8192:6000,
}
soc_info["disk_write_max"]=write_speeds[disk_capacity]
soc_info["disk_read_max"]=read_speeds[disk_capacity]
elif soc_info["name"] in ["Apple M2"]:
write_speeds={
256:1500,
512:5000,
1024:6000,
2048:6500,
4096:7500,
8192:7500,
}
read_speeds={
256:1500,
512:5500,
1024:5500,
2048:5500,
4096:6000,
8192:6000,
}
soc_info["disk_write_max"]=write_speeds[disk_capacity]
soc_info["disk_read_max"]=read_speeds[disk_capacity]
if soc_info["name"] == "Apple M1 Max":
soc_info["cpu_max_bw"] = 250
soc_info["gpu_max_bw"] = 400
Expand Down