From 40ec1807fd89435a7bf5f8e6eaf4fdb14988466f Mon Sep 17 00:00:00 2001 From: Guillaume Raffy Date: Thu, 23 Oct 2025 15:56:48 +0200 Subject: [PATCH] enriched the description of each config in stdout - the config description is now displayed in the form of a table. This allows to add the useful indicators: - amount of cache per core - ram per core - population of dimms work related to [https://bugzilla.ipr.univ-rennes.fr/show_bug.cgi?id=4171] --- concho/config.py | 49 ++++++++++++++++++++++++++++++++++++- concho/procs_chooser.py | 54 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/concho/config.py b/concho/config.py index 9b8eaf2..94d2cf1 100644 --- a/concho/config.py +++ b/concho/config.py @@ -108,7 +108,7 @@ class Cpu(Item): def __init__(self, proc_id: CpuId): super().__init__(proc_id) - cpuTable = numpy.genfromtxt('catalogs/cpu_table.tsv', dtype=("|U32", float, int, float, float, float, int, int), names=True, delimiter='\t') + cpuTable = numpy.genfromtxt('catalogs/cpu_table.tsv', dtype=("|U32", float, int, float, float, float, int, float), names=True, delimiter='\t') for cpu_id, clock, num_cores, max_cpus, tdp, cpumark, l3_cache_mb in zip(cpuTable['id'], cpuTable['clock'], cpuTable['num_cores'], cpuTable['max_cpus'], cpuTable['tdp'], cpuTable['cpumark_1_cpu'], cpuTable['l3_cache_mb']): # print(cpu_id) if cpu_id == proc_id: @@ -122,6 +122,17 @@ class Cpu(Item): self.cpumark = cpumark self.l3_cache_mb = l3_cache_mb + def as_dict(self) -> Dict: + return { + 'uid': self.uid, + 'clock': self.clock, + 'num_cores': self.num_cores, + 'max_cpus': self.max_cpus, + 'tdp': self.tdp, + 'cpumark': self.cpumark, + 'l3_cache_mb': self.l3_cache_mb + } + @property def architecture(self) -> CpuArchitecture: proc_id = self.uid @@ -437,6 +448,28 @@ class Config(): def __repr__(self) -> str: return f'Config(cpu={self.cpu.uid if self.cpu else None}, num_servers={self.num_servers}, num_cpu_per_server={self.num_cpu_per_server}, ram_size={self.ram_size} GiB)' + def as_dict(self) -> Dict: + return { + 'cpu': self.cpu.as_dict(), + 'num_servers': self.num_servers, + 'num_cpu_per_server': self.num_cpu_per_server, + 'ram_size_gib': self.ram_size, + 'cpu_slots_mem': [ + { + 'mem_channels': [ + { + 'dimms': [ + dimm.uid if dimm is not None else None + for dimm in mem_channel.dimms + ] + } + for mem_channel in cpu_slot_mem.mem_channels + ] + } + for cpu_slot_mem in self.cpu_slots_mem + ] + } + @property def chassis(self) -> ItemUid: return self.configurator.chassis.item @@ -611,6 +644,20 @@ class Config(): def num_cpus(self) -> int: return self.num_cpu_per_server * self.num_servers + @property + def ram_modules_per_server(self) -> Dict[Dimm, int]: + num_dimms: Dict[Dimm, int] = {} + for cpu_slot_mem in self.cpu_slots_mem: + for mem_channel in cpu_slot_mem.mem_channels: + for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): + slot_dimm = mem_channel.dimms[dimm_slot_index] + if slot_dimm is not None: + if slot_dimm not in num_dimms: + num_dimms[slot_dimm] = 0 + num_dimms[slot_dimm] += 1 + # assert dimm.uid == slot_dimm.uid, 'different dimms found in the configuration: %s and %s in config: %s' % (dimm.uid, slot_dimm.uid, self.as_dict()) + return num_dimms + class Configurator(): modules: Dict[ComponentId, Module] diff --git a/concho/procs_chooser.py b/concho/procs_chooser.py index ec5bee4..d219c87 100644 --- a/concho/procs_chooser.py +++ b/concho/procs_chooser.py @@ -14,6 +14,7 @@ import hashlib from concho.config import Cpu, MemSize, Configurator, Config # from concho import dell from pathlib import Path +import pandas import logging markerTypes = [',', '+', '.', '^', 'v', '<', '>', 'o', '*', '1', '2', '3', '4', '8', 's', 'p', 'h', 'H', 'x', 'X', 'D', 'd', '|', '_'] @@ -133,6 +134,9 @@ def plot_configs(configs: List[Config], xaxis_def: ConfigAxisDef, yaxis_def: Con def GHzToMHz(frequency): return frequency * 1000.0 + def ByteToMib(mem_size: MemSize) -> float: + return float(mem_size) / (1024 * 1024) + def getColorCodeFromItemLabel(label): # generation = label[-1] (num_servers, model, num_cpus, proc_id, ram_size) = re.split('_', label) @@ -211,16 +215,23 @@ def plot_configs(configs: List[Config], xaxis_def: ConfigAxisDef, yaxis_def: Con x1 = xaxis_def.get_value_for_config(config) y1 = yaxis_def.get_value_for_config(config) - logging.info(f'config {config_label}: x={x1}, y={y1}') if y1 > 0.0001: color = getColorCodeFromItemLabel(config_label) # marker = markersCycler.next() # marker = get_marker_from_label( label ) # print(x1, y1) marker = create_unique_marker(config_index) - short_label = config_label.replace('dell-poweredge-', '').replace('intel-xeon-', '').replace('hpe-proliant-', '').replace('-gen', '-g') + short_label = config_label + short_label = short_label.replace('dell-poweredge-', '') + short_label = short_label.replace('intel-xeon-', '') + short_label = short_label.replace('silver-', '') + short_label = short_label.replace('gold-', '') + short_label = short_label.replace('platinum-', '') + short_label = short_label.replace('performance-', '') + short_label = short_label.replace('hpe-proliant-', '') + short_label = short_label.replace('-gen', '-g') plt.scatter(x1, y1, facecolors=color, s=(markerSize * len(marker)), marker=r'$\mathrm{%s}$' % marker, label=short_label) - legend[marker] = short_label + legend[marker] = config, x1, y1 if False: # y1 > 5.0e16: plt.annotate(u'%s' % short_label, xy=(x1, y1), xytext=(x1 * 4.0, (y1 - 5.2e16) * 7.1), @@ -239,8 +250,43 @@ def plot_configs(configs: List[Config], xaxis_def: ConfigAxisDef, yaxis_def: Con plt.grid(visible=True, which='minor', color='b', linestyle='-', linewidth=0.2) plt.legend(bbox_to_anchor=(1.1, 1.1), ncol=2) plt.draw() + sheet = { + 'conf': [], + 'n_servers': [], + 'Chassis': [], + 'CPU ID': [], + 'cpufreq_mhz': [], + 'ncpu/ser': [], + 'Dimms per Server': [], + 'ram/ser_gb': [], + 'Total Cores': [], + 'ram/core_gb': [], + 'cache/core_mb': [], + 'Price (€)': [], + 'score/1e16': [] + } for marker in legend: - logging.info(f'config {marker}: {legend[marker]}') + config, x, y = legend[marker] + num_cores = config.cpu.num_cores * config.num_cpu_per_server * config.num_servers + cpu: Cpu = config.cpu + assert cpu.l3_cache_mb > 0, 'no cache info for cpu %s in config %s' % (cpu.uid, config.as_dict()) + cache_per_core = float(cpu.l3_cache_mb) / cpu.num_cores + sheet['conf'].append(marker) + sheet['n_servers'].append(config.num_servers) + sheet['Chassis'].append(config.chassis.uid) + sheet['CPU ID'].append(cpu.uid) + sheet['cpufreq_mhz'].append(GHzToMHz(cpu.clock)) + sheet['ncpu/ser'].append(config.num_cpu_per_server) + num_dimms = config.ram_modules_per_server + sheet['Dimms per Server'].append(' + '.join([f'{num_dimms[dimm]} x {dimm.num_gb} Gb' for dimm in num_dimms])) + sheet['ram/ser_gb'].append(float(config.ram_size / config.num_servers)) + sheet['Total Cores'].append(num_cores) + sheet['ram/core_gb'].append(float(config.ram_size) / float(num_cores)) + sheet['cache/core_mb'].append(cache_per_core) + sheet['Price (€)'].append(config.get_price()) + sheet['score/1e16'].append(y / 1.e16) + df = pandas.DataFrame(sheet) + logging.info('\n' + df.to_string(index=False)) if figure_file_path: plt.savefig(figure_file_path) else: