diff --git a/concho/config.py b/concho/config.py index a6fdcf7..f53459c 100644 --- a/concho/config.py +++ b/concho/config.py @@ -2,6 +2,7 @@ import re from abc import ABCMeta, abstractmethod import numpy from concho import dell +import math class Item(): @@ -190,7 +191,7 @@ class Config(): def __init__(self, configurator): self.configurator = configurator self.num_servers = 0 - self.num_cpu_per_server = 0 + self._num_cpu_per_server = 0 self.cpu = None self.cpu_slots_mem = [] @@ -199,6 +200,48 @@ class Config(): def chassis(self): return self.configurator.chassis.item + @staticmethod + def _find_dimm_combination(num_dimm_slots_per_channel, min_ram_per_channel, available_dimms): + available_dimms.append(Option(Dimm(0,0,'dummy'), 0.0)) # fake dimm to represent empty slot + slot_options = [] + + # try all combinations of dimms + best_slot_options = None + best_price = None + for slot_index in range(num_dimm_slots_per_channel): + slot_options.append(0) + no_more_configs = False + while no_more_configs == False: + config_capacity = 0 + config_price = 0 + for slot_index in range(num_dimm_slots_per_channel): + dimm_option = available_dimms[slot_options[slot_index]] + config_capacity += float(dimm_option.item.num_gb) * math.pow(2.0, 30.0) + config_price += dimm_option.price + if config_capacity >= min_ram_per_channel: # only remember the combination if it complies with the minimal memory constraint + if best_price is None or config_price < best_price: + best_price = config_price + best_slot_options = slot_options.copy() + # generate the next combination of dimms + for slot_index in range(num_dimm_slots_per_channel): + slot_options[slot_index] += 1 + if slot_options[slot_index] < len(available_dimms): + break + else: + if slot_index == num_dimm_slots_per_channel - 1: + no_more_configs = True # all combinations of dimm in the slots have been covered + else: + slot_options[slot_index] = 0 + + assert best_slot_options is not None, "Failed to find a dimm combination that provides %f bytes per channel." % min_ram_per_channel + slot_dimms = [] + for dimm_slot_index in range(num_dimm_slots_per_channel): + dimm = available_dimms[best_slot_options[dimm_slot_index]].item + if dimm.num_gb == 0: + dimm = None + slot_dimms.append(dimm) + return slot_dimms + def set_ram(self, ram_per_core=None, ram_per_server=None, ram_per_cpu=None): # ramUpgradePrice128Gb = { @@ -225,33 +268,14 @@ class Config(): ram_per_cpu = ram_per_server / self.num_cpu_per_server ram_per_channel = ram_per_cpu / cpu.num_ram_channels - dimm_capacity = None - if ram_per_channel > 64.0e9: - assert False, 'ram_per_channel is too big (%f bytes > 64 Gb)' % ram_per_channel - elif ram_per_channel > 32.0e9: - dimm_capacity = 64 - elif ram_per_channel > 16.0e9: - dimm_capacity = 32 - elif ram_per_channel > 8.0e9: - dimm_capacity = 16 - elif ram_per_channel > 4.0e9: - dimm_capacity = 8 - else: - try: - self.configurator.get_dimm_price(4) - dimm_capacity = 4 - except: - # 4Gb dimms are not available, use 8 Gb instead - dimm_capacity = 8 - # print('warning : forcing dimm capacity to 16G !!!!!!') - # dimm_capacity = 16 - dimm = self.configurator.get_dimm(dimm_capacity) - assert dimm - print(cpu.uid, cpu.num_cores, ram_per_channel) + + slot_dimms = Config._find_dimm_combination(self.configurator.chassis.item.num_dimm_slots_per_channel, ram_per_channel, self.configurator.get_dimm_options()) + + print(cpu.uid, cpu.num_cores, ram_per_channel, [0 if dimm is None else dimm.num_gb for dimm in slot_dimms]) 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): - mem_channel.dimms[dimm_slot_index] = dimm + mem_channel.dimms[dimm_slot_index] = slot_dimms[dimm_slot_index] @property def ram_size(self): @@ -293,20 +317,38 @@ class Config(): return flops - def set_cpu(self, cpu): - self.cpu = cpu + def _init_dimm_slots(self): # create the dimm slots self.cpu_slots_mem = [] + + if self.cpu is None: + return + for cpu_index in range(self.num_cpu_per_server): cpu_slot_mem = CpuSlotMem() - for channel_index in range(cpu.num_ram_channels): + for channel_index in range(self.cpu.num_ram_channels): mem_channel = MemChannel() for dimm_slot_index in range(self.configurator.chassis.item.num_dimm_slots_per_channel): mem_channel.dimms.append(None) # dimm slots are empty cpu_slot_mem.mem_channels.append(mem_channel) self.cpu_slots_mem.append(cpu_slot_mem) + def set_cpu(self, cpu): + self.cpu = cpu + # update the dimm slots accordingly + self._init_dimm_slots() + + @property + def num_cpu_per_server(self): + return self._num_cpu_per_server + + @num_cpu_per_server.setter + def num_cpu_per_server(self, num_cpu_per_server): + self._num_cpu_per_server = num_cpu_per_server + # update the dimm slots accordingly + self._init_dimm_slots() + @property def num_cpus(self): return self.num_cpu_per_server * self.num_servers @@ -353,6 +395,9 @@ class Configurator(): return dimm assert False, 'failed to find an option for a dimm of capacity %d gb' % dimm_capacity + def get_dimm_options(self): + return list(self.modules['ram'].options.values()) + def get_item(self, item_uid): for module in self.modules.values(): if item_uid in module.options: diff --git a/concho/procs_chooser.py b/concho/procs_chooser.py index 523f719..847f24b 100644 --- a/concho/procs_chooser.py +++ b/concho/procs_chooser.py @@ -142,9 +142,9 @@ def plot_system_efficiency(): if not cpu.architecture in ['coffeelake', 'skylake','cascadelake']: continue config = configurator.create_config() + config.num_cpu_per_server = config.configurator.chassis.item.num_cpu_slots_per_server config.set_cpu(cpu) config.set_ram(ram_per_core=4.0e9) - config.num_cpu_per_server = config.configurator.chassis.item.num_cpu_slots_per_server item_label = numpy.append( item_label, config.chassis.uid + '_' + cpu.uid + '_' + str(config.ram_size) + 'gb' ) # print('procOptionPrice', procOptionPrice) # config_price = procOptionPrice