From 23e05b7c8a672d8a601863a9b5278b4ed19ace2c Mon Sep 17 00:00:00 2001 From: "Craig R. Hughes" Date: Wed, 17 Apr 2024 21:50:57 -0700 Subject: [PATCH 1/2] M3 Max power and bandwidth data (for the M3 Max 128GB version anyway - power levels measured on device using powermetrics and maxing out CPU/GPU usage) --- asitop/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/asitop/utils.py b/asitop/utils.py index 1e7b9ce..868f068 100644 --- a/asitop/utils.py +++ b/asitop/utils.py @@ -163,6 +163,9 @@ def get_soc_info(): elif soc_info["name"] == "Apple M2": soc_info["cpu_max_power"] = 25 soc_info["gpu_max_power"] = 15 + elif soc_info["name"] == "Apple M3 Max": + soc_info["cpu_max_power"] = 54 + soc_info["gpu_max_power"] = 47 else: soc_info["cpu_max_power"] = 20 soc_info["gpu_max_power"] = 20 @@ -182,6 +185,9 @@ def get_soc_info(): elif soc_info["name"] == "Apple M2": soc_info["cpu_max_bw"] = 100 soc_info["gpu_max_bw"] = 100 + elif soc_info["name"] == "Apple M3 Max": + soc_info["cpu_max_bw"] = 400 + soc_info["gpu_max_bw"] = 400 else: soc_info["cpu_max_bw"] = 70 soc_info["gpu_max_bw"] = 70 From 2d4cc4a554c49632c4683fd6ae85f16d7a9dc831 Mon Sep 17 00:00:00 2001 From: "Craig R. Hughes" Date: Wed, 17 Apr 2024 21:55:28 -0700 Subject: [PATCH 2/2] Do a better job generically calculating E-core and P-core activity to work on all processors. Do not use the cluster info from powermetrics because it seems to just be wrong on later chips like M3. Instead, calculate averages from the individual cores. For the MHz, weight the MHz of each core by that core's activity level; I don't know if this is the right approach here - but this will give a sense of roughly the MHz of the "average" chip that's actually doing something, so idle cores at low MHz won't drag the number way down when a few cores are cranking at high MHz/100% utilization. --- asitop/asitop.py | 34 ++++++++++++++++++++-------------- asitop/parsers.py | 44 ++++++++++++++------------------------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/asitop/asitop.py b/asitop/asitop.py index 0845798..cce7025 100644 --- a/asitop/asitop.py +++ b/asitop/asitop.py @@ -37,15 +37,21 @@ def main(): e_core_count = soc_info_dict["e_core_count"] e_core_gauges = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(e_core_count)] p_core_count = soc_info_dict["p_core_count"] - p_core_gauges = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(min(p_core_count, 8))] - p_core_split = [HSplit( - *p_core_gauges, - )] if p_core_count > 8: - p_core_gauges_ext = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(p_core_count - 8)] + p_core_gauges = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(min(p_core_count, int(p_core_count/2)))] + p_core_split = [HSplit( + *p_core_gauges, + )] + p_core_gauges_ext = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(p_core_count - int(p_core_count/2))] p_core_split.append(HSplit( *p_core_gauges_ext, )) + else: + p_core_gauges = [VGauge(val=0, color=args.color, border_color=args.color) for _ in range(min(p_core_count, 8))] + p_core_split = [HSplit( + *p_core_gauges, + )] + processor_gauges = [cpu1_gauge, HSplit(*e_core_gauges), cpu2_gauge, @@ -194,18 +200,18 @@ def get_avg(inlist): cpu1_gauge.title = "".join([ "E-CPU Usage: ", - str(cpu_metrics_dict["E-Cluster_active"]), + '%0.2f' % cpu_metrics_dict["E-Cluster_active"], "% @ ", - str(cpu_metrics_dict["E-Cluster_freq_Mhz"]), + '%d' % cpu_metrics_dict["E-Cluster_freq_Mhz"], " MHz" ]) cpu1_gauge.value = cpu_metrics_dict["E-Cluster_active"] cpu2_gauge.title = "".join([ "P-CPU Usage: ", - str(cpu_metrics_dict["P-Cluster_active"]), + '%0.2f' % cpu_metrics_dict["P-Cluster_active"], "% @ ", - str(cpu_metrics_dict["P-Cluster_freq_Mhz"]), + '%d' % cpu_metrics_dict["P-Cluster_freq_Mhz"], " MHz" ]) cpu2_gauge.value = cpu_metrics_dict["P-Cluster_active"] @@ -215,20 +221,20 @@ def get_avg(inlist): for i in cpu_metrics_dict["e_core"]: e_core_gauges[core_count % 4].title = "".join([ "Core-" + str(i + 1) + " ", - str(cpu_metrics_dict["E-Cluster" + str(i) + "_active"]), + '%0.2f' % cpu_metrics_dict["E-Cluster" + str(i) + "_active"], "%", ]) e_core_gauges[core_count % 4].value = cpu_metrics_dict["E-Cluster" + str(i) + "_active"] core_count += 1 core_count = 0 for i in cpu_metrics_dict["p_core"]: - core_gauges = p_core_gauges if core_count < 8 else p_core_gauges_ext - core_gauges[core_count % 8].title = "".join([ + core_gauges = p_core_gauges if core_count < int(p_core_count/2) else p_core_gauges_ext + core_gauges[core_count % int(p_core_count/2)].title = "".join([ ("Core-" if p_core_count < 6 else 'C-') + str(i + 1) + " ", - str(cpu_metrics_dict["P-Cluster" + str(i) + "_active"]), + '%0.2f' % cpu_metrics_dict["P-Cluster" + str(i) + "_active"], "%", ]) - core_gauges[core_count % 8].value = cpu_metrics_dict["P-Cluster" + str(i) + "_active"] + core_gauges[core_count % int(p_core_count/2)].value = cpu_metrics_dict["P-Cluster" + str(i) + "_active"] core_count += 1 gpu_gauge.title = "".join([ diff --git a/asitop/parsers.py b/asitop/parsers.py index d3530d4..df4dea0 100644 --- a/asitop/parsers.py +++ b/asitop/parsers.py @@ -84,8 +84,6 @@ def parse_cpu_metrics(powermetrics_parse): cpu_clusters = cpu_metrics["clusters"] for cluster in cpu_clusters: name = cluster["name"] - cpu_metric_dict[name+"_freq_Mhz"] = int(cluster["freq_hz"]/(1e6)) - cpu_metric_dict[name+"_active"] = int((1 - cluster["idle_ratio"])*100) for cpu in cluster["cpus"]: name = 'E-Cluster' if name[0] == 'E' else 'P-Cluster' core = e_core if name[0] == 'E' else p_core @@ -94,34 +92,20 @@ def parse_cpu_metrics(powermetrics_parse): cpu_metric_dict[name + str(cpu["cpu"]) + "_active"] = int((1 - cpu["idle_ratio"]) * 100) cpu_metric_dict["e_core"] = e_core cpu_metric_dict["p_core"] = p_core - if "E-Cluster_active" not in cpu_metric_dict: - # M1 Ultra - cpu_metric_dict["E-Cluster_active"] = int( - (cpu_metric_dict["E0-Cluster_active"] + cpu_metric_dict["E1-Cluster_active"])/2) - if "E-Cluster_freq_Mhz" not in cpu_metric_dict: - # M1 Ultra - cpu_metric_dict["E-Cluster_freq_Mhz"] = max( - cpu_metric_dict["E0-Cluster_freq_Mhz"], cpu_metric_dict["E1-Cluster_freq_Mhz"]) - if "P-Cluster_active" not in cpu_metric_dict: - if "P2-Cluster_active" in cpu_metric_dict: - # M1 Ultra - cpu_metric_dict["P-Cluster_active"] = int((cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"] + - cpu_metric_dict["P2-Cluster_active"] + cpu_metric_dict["P3-Cluster_active"]) / 4) - else: - cpu_metric_dict["P-Cluster_active"] = int( - (cpu_metric_dict["P0-Cluster_active"] + cpu_metric_dict["P1-Cluster_active"])/2) - if "P-Cluster_freq_Mhz" not in cpu_metric_dict: - if "P2-Cluster_freq_Mhz" in cpu_metric_dict: - # M1 Ultra - freqs = [ - cpu_metric_dict["P0-Cluster_freq_Mhz"], - cpu_metric_dict["P1-Cluster_freq_Mhz"], - cpu_metric_dict["P2-Cluster_freq_Mhz"], - cpu_metric_dict["P3-Cluster_freq_Mhz"]] - cpu_metric_dict["P-Cluster_freq_Mhz"] = max(freqs) - else: - cpu_metric_dict["P-Cluster_freq_Mhz"] = max( - cpu_metric_dict["P0-Cluster_freq_Mhz"], cpu_metric_dict["P1-Cluster_freq_Mhz"]) + freq_sum = 0 + active_sum = 0 + for cpu in e_core: + freq_sum += cpu_metric_dict['E-Cluster' + str(cpu) + '_freq_Mhz'] * cpu_metric_dict['E-Cluster' + str(cpu) + '_active']/100 + active_sum += cpu_metric_dict['E-Cluster' + str(cpu) + '_active'] + cpu_metric_dict['E-Cluster_freq_Mhz'] = freq_sum / (active_sum/100) + cpu_metric_dict['E-Cluster_active'] = active_sum / len(e_core) + freq_sum = 0 + active_sum = 0 + for cpu in p_core: + freq_sum += cpu_metric_dict['P-Cluster' + str(cpu) + '_freq_Mhz'] * cpu_metric_dict['P-Cluster' + str(cpu) + '_active']/100 + active_sum += cpu_metric_dict['P-Cluster' + str(cpu) + '_active'] + cpu_metric_dict['P-Cluster_freq_Mhz'] = freq_sum / (active_sum/100) + cpu_metric_dict['P-Cluster_active'] = active_sum / len(p_core) # power cpu_metric_dict["ane_W"] = cpu_metrics["ane_energy"]/1000 #cpu_metric_dict["dram_W"] = cpu_metrics["dram_energy"]/1000