From ed11319d85ecd3df8e9afbd7aa81840f5db4e378 Mon Sep 17 00:00:00 2001 From: Derek Feichtinger Date: Tue, 30 Jun 2026 15:54:14 +0200 Subject: [PATCH] introduce a waste_total metric that normalizes CPU and Mem waste --- slurm-eff-tool.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/slurm-eff-tool.py b/slurm-eff-tool.py index dc94043..576cb6f 100755 --- a/slurm-eff-tool.py +++ b/slurm-eff-tool.py @@ -84,6 +84,7 @@ ALL_COLUMNS = [ "Walltime", "Walltime_max", "Time_Eff", + "waste_total", "jobname", ] @@ -157,6 +158,21 @@ PRESET_COLUMNS = { "Mem_Eff", "waste_Mem", "jobname",], + "waste": [ + "username", + "JobID", + "state", + "Count", + "NTasks", + "CPUs", + "Nodes", + "CPU_Eff", + "Mem_Eff", + "Time_Eff", + "waste_CPU", + "waste_Mem", + "waste_total", + "jobname",], } # One-character aliases for sorting and output format specifications. @@ -176,6 +192,7 @@ ALIASES = { "j": "jobname", "X": "waste_CPU", "Y": "waste_Mem", + "T": "waste_total", } NUMERIC_COLUMNS = { @@ -195,6 +212,7 @@ NUMERIC_COLUMNS = { "Walltime_max", "waste_CPU", "waste_Mem", + "waste_total", } state_mappings = { @@ -419,6 +437,7 @@ class OutputRow: walltime_max: float | None # in h waste_CPU: float | None # CPU h waste_Mem: float | None + waste_total: float | None _cpu_eff_values: list[float] = field(default_factory=list, repr=False) _mem_eff_values: list[float] = field(default_factory=list, repr=False) _time_eff_values: list[float] = field(default_factory=list, repr=False) @@ -446,6 +465,7 @@ class OutputRow: "jobname": self.jobname, "waste_CPU": self.waste_CPU, "waste_Mem": self.waste_Mem, + "waste_total": self.waste_total } if sdev: for col, vals in [ @@ -893,12 +913,12 @@ def make_single_row(rec: JobRecord, dflt_mpcpu: float) -> OutputRow: """Returns an OutputRow based on a single slurm job record.""" walltime = rec.elapsed_sec / 3600 - waste_mem = None + waste_mem = 0 if rec.mem_eff is not None and rec.reqmem_gb is not None: waste_mem = max(0,walltime * (100-rec.mem_eff)/100 \ * (rec.reqmem_gb - rec.cpus * dflt_mpcpu)) - waste_cpu = None + waste_cpu = 0 if rec.cpu_eff is not None: # We count failed jobs as wasted CPU! if rec.state != state_mappings['COMPLETED']: @@ -906,6 +926,8 @@ def make_single_row(rec: JobRecord, dflt_mpcpu: float) -> OutputRow: else: waste_cpu = walltime * (100-rec.cpu_eff)/100 * rec.cpus + waste_total = waste_cpu + waste_mem/dflt_mpcpu + jobid = rec.jobidraw if jobid == "": jobid = rec.jobid @@ -932,6 +954,7 @@ def make_single_row(rec: JobRecord, dflt_mpcpu: float) -> OutputRow: walltime_max= walltime, waste_Mem=waste_mem, waste_CPU=waste_cpu, + waste_total=waste_total, _cpu_eff_values=[rec.cpu_eff] if rec.cpu_eff is not None else [], _mem_eff_values=[rec.mem_eff] if rec.mem_eff is not None else [], _time_eff_values=[rec.time_eff] if rec.time_eff is not None else [], @@ -960,7 +983,7 @@ def make_aggregate_row(records: list[JobRecord], username: str, memory_efficiency=mean_or_none(mem_eff_vals) count=len(records) - waste_mem = None + waste_mem = 0 if (memory_efficiency is not None) and (walltime is not None) \ and first.reqmem_gb is not None: waste_mem = max(0,count * walltime * (100-memory_efficiency)/100 \ @@ -968,7 +991,7 @@ def make_aggregate_row(records: list[JobRecord], username: str, cpu_efficiency = mean_or_none(cpu_eff_vals) - waste_cpu=None + waste_cpu=0 if cpu_efficiency is not None and walltime is not None: # We count failed jobs as wasted CPU! if first.state != state_mappings['COMPLETED']: @@ -976,6 +999,8 @@ def make_aggregate_row(records: list[JobRecord], username: str, else: waste_cpu = count * walltime * (100-cpu_efficiency)/100 * first.cpus + waste_total = waste_cpu + waste_mem/dflt_mpcpu + return OutputRow( username=username, JobID="", @@ -998,6 +1023,7 @@ def make_aggregate_row(records: list[JobRecord], username: str, walltime_max=max([r.elapsed_sec for r in records if r.elapsed_sec is not None]) / 3600, waste_Mem=waste_mem, waste_CPU=waste_cpu, + waste_total=waste_total, _cpu_eff_values=cpu_eff_vals, _mem_eff_values=mem_eff_vals, _time_eff_values=time_eff_vals,