diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c8b0e23..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index a0d8bfa..1a26ec3 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,6 @@ dmypy.json # idea .idea/ +launch.py +.vscode/launch.json +.DS_Store diff --git a/asitop/asitop.py b/asitop/asitop.py index 0845798..bbb8dfe 100644 --- a/asitop/asitop.py +++ b/asitop/asitop.py @@ -62,29 +62,28 @@ 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" ) @@ -116,7 +115,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"], @@ -182,7 +181,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 @@ -277,74 +276,47 @@ 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" - ]) - 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_rate=disk_metrics_dict["read_rate"] + disk_read_chart.title = "".join([ + "Disk Read: ", + '{0:.1f}'.format(disk_read_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" + 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" ]) - 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:" - """ + package_power_W = cpu_metrics_dict["package_W"] / \ args.interval diff --git a/asitop/parsers.py b/asitop/parsers.py index d3530d4..ad6238f 100644 --- a/asitop/parsers.py +++ b/asitop/parsers.py @@ -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", @@ -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 = [] @@ -124,7 +122,7 @@ 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["combined_power"]/1000 diff --git a/asitop/utils.py b/asitop/utils.py index 1e7b9ce..9ebd306 100644 --- a/asitop/utils.py +++ b/asitop/utils.py @@ -17,10 +17,11 @@ 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 = None + # 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: @@ -28,10 +29,11 @@ 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 = None + # 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 @@ -48,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", + "--samplers cpu_power,gpu_power,thermal,disk,network", output_file_flag, "/tmp/asitop_powermetrics"+timecode, "-f plist", @@ -103,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() @@ -147,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 @@ -167,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