refactored because the code was hard to maintain and was hacked in lots of ways (handling of 4 servers per host).
- the prices are now retrived from matinfo's downloaded pages - the derived prices (such as the price without cpu) are now automatically computed, rather than hardcoded from (hand computation) - although it's a bit dirty, the architecture of the code is more generic: - each configuration is now stored in a Config object, which is able to compute its price. - dell specific code is now decoupled into a separate file. warnings: - the refactoring is designed to handle the old price tables but this work is not finished (the code using the old price tables is broken at the moment) - the computed price is currently overestimated, as the price of the base memory dimms should be substracted - disks are not currently handled
This commit is contained in:
		
							parent
							
								
									d2fb11551e
								
							
						
					
					
						commit
						93a8b77231
					
				|  | @ -0,0 +1,371 @@ | ||||||
|  | import re | ||||||
|  | from abc import ABCMeta, abstractmethod | ||||||
|  | import numpy | ||||||
|  | from concho import dell | ||||||
|  | 
 | ||||||
|  | class Item(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, uid): | ||||||
|  |         self.uid = uid | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Chassis(Item): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, uid): | ||||||
|  |         super().__init__(uid) | ||||||
|  |         self.max_num_servers = 1 | ||||||
|  |         self.num_cpu_slots_per_server = 2 | ||||||
|  |         if re.match('dell-poweredge-r9.*', uid): | ||||||
|  |             self.num_cpu_slots_per_server = 4 | ||||||
|  | 
 | ||||||
|  | class Dimm(Item): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, num_gb, num_mhz, mem_type): | ||||||
|  |         uid = "%s-%s-%s" % (mem_type, num_gb, num_mhz) | ||||||
|  |         super().__init__(uid) | ||||||
|  |         self.num_gb = num_gb | ||||||
|  |         self.num_mhz = num_mhz | ||||||
|  |         self.mem_type = mem_type | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Cpu(Item): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, proc_id): | ||||||
|  |         super().__init__(proc_id) | ||||||
|  |         cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U32", float, int, float, float, float), names=True, delimiter='\t') | ||||||
|  |         for cpu_id, clock, num_cores, tdp, cpumark in zip(cpuTable['id'], cpuTable['clock'], cpuTable['num_cores'], cpuTable['tdp'], cpuTable['cpumark_1_cpu']): | ||||||
|  |             print(cpu_id) | ||||||
|  |             if cpu_id == proc_id: | ||||||
|  |                 # print('found '+procId) | ||||||
|  |                 break | ||||||
|  |         assert cpu_id == proc_id, 'Failed to find %s in cputable' % proc_id | ||||||
|  |         self.clock = clock | ||||||
|  |         self.num_cores = num_cores | ||||||
|  |         self.tdp = tdp | ||||||
|  |         self.cpumark = cpumark | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def architecture(self): | ||||||
|  |         proc_id = self.uid | ||||||
|  |         if re.match('intel-core-i[357]-8[0-9][0-9][0-9][ktbuh]', proc_id): | ||||||
|  |             return 'coffeelake' | ||||||
|  |         elif re.match('intel-xeon-silver-[0-9]2[0-9][0-9]', proc_id): | ||||||
|  |             return 'cascadelake' | ||||||
|  |         elif re.match('intel-xeon-gold-[0-9]2[0-9][0-9]', proc_id): | ||||||
|  |             return 'cascadelake' | ||||||
|  |         elif re.match('intel-xeon-platinum-[0-9]2[0-9][0-9]', proc_id): | ||||||
|  |             return 'cascadelake' | ||||||
|  |         elif re.match('intel-xeon-gold-[0-9]1[0-9][0-9]', proc_id): | ||||||
|  |             return 'skylake' | ||||||
|  |         elif re.match('intel-xeon-platinum-[0-9]1[0-9][0-9]', proc_id): | ||||||
|  |             return 'skylake' | ||||||
|  |         elif re.match('intel-xeon-e5-26[0-9][0-9][lwa]*v4', proc_id): | ||||||
|  |             return 'broadwell' | ||||||
|  |         elif re.match('intel-xeon-e5-26[0-9][0-9][lwa]*v3', proc_id): | ||||||
|  |             return 'haswell' | ||||||
|  |         elif re.match('intel-xeon-e5-26[0-9][0-9][lwa]*v2', proc_id): | ||||||
|  |             return 'ivy bridge' | ||||||
|  |         elif re.match('intel-xeon-e5-26[0-9][0-9][lwa]*', proc_id): | ||||||
|  |             return 'sandy bridge' | ||||||
|  |         elif re.match('intel-xeon-x56[0-9][0-9]', proc_id): | ||||||
|  |             return 'gulftown' | ||||||
|  |         elif re.match('intel-xeon-x55[0-9][0-9]', proc_id): | ||||||
|  |             return 'gainestown' | ||||||
|  |         elif re.match('intel-xeon-e54[0-9][0-9]', proc_id): | ||||||
|  |             return 'harpertown' | ||||||
|  |         elif re.match('intel-xeon-51[0-9][0-9]', proc_id): | ||||||
|  |             return 'woodcrest' | ||||||
|  |         else: | ||||||
|  |             assert False, 'unhandled processor id : %s' % proc_id | ||||||
|  |      | ||||||
|  |     @property | ||||||
|  |     def num_dp_flop_per_cycle(self): | ||||||
|  |         proc_arch = self.architecture | ||||||
|  |         simd_id = get_simd_id(proc_arch) | ||||||
|  |         num_simd_per_core = 1 | ||||||
|  |         if proc_arch == 'skylake' or proc_arch == 'cascadelake': | ||||||
|  |             # from https://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors : Xeon Platinum, Gold 61XX, and Gold 5122 have two AVX-512 FMA units per core; Xeon Gold 51XX (except 5122), Silver, and Bronze have a single AVX-512 FMA unit per core | ||||||
|  |             if re.match('intel-xeon-gold-5122', self.uid): | ||||||
|  |                 num_simd_per_core = 2 | ||||||
|  | 
 | ||||||
|  |             # https://en.wikichip.org/wiki/intel/xeon_gold/5222 : 'Note that this is the only processor in the Xeon Gold 52xx series with two 512b FMA units.' | ||||||
|  |             if re.match('intel-xeon-gold-5222', self.uid): | ||||||
|  |                 num_simd_per_core = 2 | ||||||
|  |                  | ||||||
|  |             if re.match('intel-xeon-gold-61[0-9][0-9]', self.uid): | ||||||
|  |                 num_simd_per_core = 2 | ||||||
|  |             if re.match('intel-xeon-gold-62[0-9][0-9]', self.uid): | ||||||
|  |                 num_simd_per_core = 2 | ||||||
|  |         dp_flops_per_cycle = num_simd_per_core * simd_id_to_dp_flops_per_cycle(simd_id) | ||||||
|  |         print(self.uid, dp_flops_per_cycle) | ||||||
|  |         return dp_flops_per_cycle | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def num_ram_channels(self): | ||||||
|  |         return { | ||||||
|  |             'skylake': 6, | ||||||
|  |             'coffeelake': 6, | ||||||
|  |             'cascadelake': 6 | ||||||
|  |         }[self.architecture] | ||||||
|  | 
 | ||||||
|  | def get_proc_architecture(proc_id): | ||||||
|  |     return Cpu(proc_id).architecture | ||||||
|  | 
 | ||||||
|  | def get_proc_arch_transistor_size(proc_id):  | ||||||
|  |     return { | ||||||
|  |         'woodcrest':65, | ||||||
|  |         'harpertown':45, | ||||||
|  |         'gainestown':45, | ||||||
|  |         'gulftown':32, | ||||||
|  |         'sandy bridge':32, | ||||||
|  |         'ivy bridge':22, | ||||||
|  |         'haswell':22, | ||||||
|  |         'broadwell':14, | ||||||
|  |         'skylake':14, | ||||||
|  |         'coffeelake':14, | ||||||
|  |         'cascadelake':14 | ||||||
|  |         }[get_proc_architecture(proc_id)] | ||||||
|  | 
 | ||||||
|  | def simd_id_to_dp_flops_per_cycle(simd_id): | ||||||
|  |     """ | ||||||
|  |     :param str simd_id: eg 'avx2' | ||||||
|  |      | ||||||
|  |     """ | ||||||
|  |     # from https://stackoverflow.com/questions/15655835/flops-per-cycle-for-sandy-bridge-and-haswell-sse2-avx-avx2 | ||||||
|  |     # Intel Core 2 and Nehalem: | ||||||
|  |     #  | ||||||
|  |     #     4 DP FLOPs/cycle: 2-wide SSE2 addition + 2-wide SSE2 multiplication | ||||||
|  |     #     8 SP FLOPs/cycle: 4-wide SSE addition + 4-wide SSE multiplication | ||||||
|  |     #  | ||||||
|  |     # Intel Sandy Bridge/Ivy Bridge: | ||||||
|  |     #  | ||||||
|  |     #     8 DP FLOPs/cycle: 4-wide AVX addition + 4-wide AVX multiplication | ||||||
|  |     #     16 SP FLOPs/cycle: 8-wide AVX addition + 8-wide AVX multiplication | ||||||
|  |     #  | ||||||
|  |     # Intel Haswell/Broadwell/Skylake/Kaby Lake: | ||||||
|  |     #  | ||||||
|  |     #     16 DP FLOPs/cycle: two 4-wide FMA (fused multiply-add) instructions | ||||||
|  |     #     32 SP FLOPs/cycle: two 8-wide FMA (fused multiply-add) instructions | ||||||
|  | 
 | ||||||
|  |     return {     | ||||||
|  |         'sse4.1':4, | ||||||
|  |         'sse4.2':4, | ||||||
|  |         'avx':8, | ||||||
|  |         'avx2':16, | ||||||
|  |         'avx-512':16, | ||||||
|  |         }[simd_id] | ||||||
|  |      | ||||||
|  | def get_simd_id(proc_arch): | ||||||
|  |     """ | ||||||
|  |         :param str proc_arch: eg 'broadwell' | ||||||
|  |         :return str: eg 'sse4' | ||||||
|  |     """ | ||||||
|  |     return { | ||||||
|  |         'woodcrest':'sse4.1', | ||||||
|  |         'harpertown':'sse4.1', | ||||||
|  |         'gainestown':'sse4.2', | ||||||
|  |         'gulftown':'sse4.2', | ||||||
|  |         'sandy bridge':'avx', | ||||||
|  |         'ivy bridge':'avx', | ||||||
|  |         'haswell':'avx2', | ||||||
|  |         'broadwell':'avx2', | ||||||
|  |         'skylake':'avx-512', | ||||||
|  |         'cascadelake':'avx-512', | ||||||
|  |         'coffeelake':'avx2' | ||||||
|  |         }[proc_arch] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Config(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, configurator): | ||||||
|  |         self.configurator = configurator | ||||||
|  |         self.num_servers = 0 | ||||||
|  |         self.num_cpu_per_server = 0 | ||||||
|  |         self.cpu = None | ||||||
|  |         self.dimms_per_channel = [] | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def chassis(self): | ||||||
|  |         return self.configurator.chassis.item | ||||||
|  | 
 | ||||||
|  |     def set_ram(self, ram_per_core=None, ram_per_server=None, ram_per_cpu=None): | ||||||
|  | 
 | ||||||
|  |         # ramUpgradePrice128Gb = { | ||||||
|  |         #     'c6220':3520.0, | ||||||
|  |         #     'r620':2010.0,  | ||||||
|  |         #     'r630':1778.0, | ||||||
|  |         #     'r640':1780.0, | ||||||
|  |         #     'r730':1778.0, | ||||||
|  |         #     'r940':960.0,   # 32 Gb 2933 MHz RDIMM : 320 € | ||||||
|  |         #     'c6320':6222.6, | ||||||
|  |         #     'c4310':1778.0, | ||||||
|  |         #     'precision3630': 1536.0 } | ||||||
|  |         cpu = self.cpu | ||||||
|  |         if ram_per_cpu: | ||||||
|  |             assert not ram_per_core | ||||||
|  |             assert not ram_per_server | ||||||
|  |         if ram_per_core: | ||||||
|  |             assert not ram_per_server | ||||||
|  |             assert not ram_per_cpu | ||||||
|  |             ram_per_cpu = cpu.num_cores * ram_per_core | ||||||
|  |         if ram_per_server: | ||||||
|  |             assert not ram_per_core | ||||||
|  |             assert not ram_per_cpu | ||||||
|  |             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 | ||||||
|  |         self.dimms_per_channel = [ dimm ] | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |     def get_price(self): | ||||||
|  |         price = self.configurator.chassis.price | ||||||
|  |         price += self.num_servers * self.num_cpu_per_server * self.configurator.get_item_price(self.cpu.uid) | ||||||
|  |         for dimm in  self.dimms_per_channel: | ||||||
|  |             dimm_price = self.configurator.get_item_price(dimm.uid) | ||||||
|  |             price += self.num_servers * self.num_cpu_per_server * self.cpu.num_ram_channels * dimm_price | ||||||
|  |         # print("ram_price : %f € for %d dimms of %d Gb" % (ram_price, self.num_servers * self.num_cpu_per_server * cpu.num_ram_channels, dimm_capacity)) | ||||||
|  |         assert price > 0.0 | ||||||
|  |         return price | ||||||
|  | 
 | ||||||
|  |     def get_power_consumption(self): | ||||||
|  |         server_base_power_consumption = 100.0 # rough estimation in watts | ||||||
|  |         power_consumption = (self.cpu.tdp * self.num_cpu_per_server + server_base_power_consumption) * self.num_servers | ||||||
|  |         return power_consumption | ||||||
|  | 
 | ||||||
|  |     def get_flops(self): | ||||||
|  |         flops = self.cpu.num_dp_flop_per_cycle * self.cpu.clock * 1.e9 * self.cpu.num_cores * self.num_cpu_per_server * self.num_servers | ||||||
|  |         return flops | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def set_cpu(self, cpu): | ||||||
|  |         self.cpu = cpu | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Option(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, item, price): | ||||||
|  |         self.item = item | ||||||
|  |         self.price = price | ||||||
|  | 
 | ||||||
|  | class Module(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, name): | ||||||
|  |         self.name = name | ||||||
|  |         self.options = {} | ||||||
|  | 
 | ||||||
|  |     def add_option(self, option): | ||||||
|  |         self.options[option.item.uid] = option | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Configurator(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, name): | ||||||
|  |         self.modules = {} | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def create_config(self): | ||||||
|  |         assert False | ||||||
|  | 
 | ||||||
|  |     def add_module(self, module): | ||||||
|  |         self.modules[module.name] = module | ||||||
|  | 
 | ||||||
|  |     def get_cpu_options(self): | ||||||
|  |         return [Cpu(option.item.uid) for option in self.modules['processor'].options.values()] | ||||||
|  | 
 | ||||||
|  |     def get_ram_options(self): | ||||||
|  |         return self.modules['ram'].values() | ||||||
|  | 
 | ||||||
|  |     def get_dimm(self, dimm_capacity): | ||||||
|  |         for dimm_option in self.modules['ram'].options.values(): | ||||||
|  |             dimm = dimm_option.item | ||||||
|  |             # print(dimm.num_gb) | ||||||
|  |             if dimm.num_gb == dimm_capacity: | ||||||
|  |                 return dimm | ||||||
|  |         assert False, 'failed to find an option for a dimm of capacity %d gb' % dimm_capacity | ||||||
|  | 
 | ||||||
|  |     def get_item_price(self, item_uid): | ||||||
|  |         for module in self.modules.values(): | ||||||
|  |             if item_uid in module.options: | ||||||
|  |                 return module.options[item_uid].price | ||||||
|  | 
 | ||||||
|  | class TableBasedConfigurator(Configurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, host_type_id, num_cpu_per_server, num_servers=1): | ||||||
|  |         self.host_type_id = host_type_id | ||||||
|  |         self.num_cpu_per_server = num_cpu_per_server | ||||||
|  |         self.num_servers = num_servers | ||||||
|  |         self.dell_price_table = numpy.genfromtxt('dell_procoptions_table.dat', dtype=("|U15", "|U15", float), names=True, delimiter='\t') | ||||||
|  |         self.base_config = Config(self) | ||||||
|  |         self.base_config.num_servers = self.num_servers | ||||||
|  |         self.base_config.num_cpu_per_server = self.num_cpu_per_server | ||||||
|  |          | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_dimm_price(self, dimm_capacity): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_disk_upgrade_price(self, disk_capacity): | ||||||
|  |         pass | ||||||
|  |              | ||||||
|  |     def get_cpu_options(self): | ||||||
|  |         supported_cpus = [] | ||||||
|  |         for host_type_id, proc_id, proc_option_price in zip(self.dell_price_table['host_type_id'], self.dell_price_table['proc_id'], self.dell_price_table['proc_option_price']): | ||||||
|  |             if host_type_id == self.host_type_id: | ||||||
|  |                 supported_cpus.append(Cpu(proc_id)) | ||||||
|  |         return supported_cpus | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # def create_host_type(host_type_id): | ||||||
|  | #     if host_type_id == 'c6420': | ||||||
|  | #         return dell.DellPowerEdgeC6420(host_type_id) | ||||||
|  | #     if host_type_id == 'c6320': | ||||||
|  | #         return dell.DellPowerEdgeC6320(host_type_id) | ||||||
|  | #     if host_type_id == 'c4130': | ||||||
|  | #         return dell.DellPowerEdgeC4130(host_type_id) | ||||||
|  | #     if host_type_id == 'r620': | ||||||
|  | #         return dell.DellPowerEdgeR620(host_type_id) | ||||||
|  | #     if host_type_id == 'r630': | ||||||
|  | #         return dell.DellPowerEdgeR630(host_type_id) | ||||||
|  | #     if host_type_id == 'r640': | ||||||
|  | #         return dell.DellPowerEdgeR640(host_type_id) | ||||||
|  | #     if host_type_id == 'r940': | ||||||
|  | #         return dell.DellPowerEdgeR940(host_type_id) | ||||||
|  | #     if host_type_id == 'precision3630': | ||||||
|  | #         return dell.DellPrecision3630(host_type_id) | ||||||
|  | #     assert False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     # dom = parse(dell_configurator_html_file_path) | ||||||
|  | @ -0,0 +1,587 @@ | ||||||
|  | from concho.config import TableBasedConfigurator | ||||||
|  | from concho.config import Configurator | ||||||
|  | from concho.config import Module | ||||||
|  | from concho.config import Option | ||||||
|  | from concho.config import Config | ||||||
|  | from concho.config import Chassis | ||||||
|  | from concho.config import Cpu, Dimm | ||||||
|  | 
 | ||||||
|  | from abc import ABCMeta, abstractmethod | ||||||
|  | from xml.dom.minidom import parse | ||||||
|  | from lxml.html import parse | ||||||
|  | import re | ||||||
|  | import copy | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeC6220(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('c6220', num_cpu_per_server=2, num_servers=4) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         return 4890.0 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 880.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 320.0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeR620(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('r620', num_cpu_per_server=2, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         return 860.0 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 240.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return -20.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeR630(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('r630', num_cpu_per_server=2, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # for r630 on 14/10/2016 | ||||||
|  |         #   (x: price without procs, p2603: price of e5-2603v4, p2609: price of e5-2609v4) | ||||||
|  |         #   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 | ||||||
|  |         #   x + p2603 = 948.0 | ||||||
|  |         #   x + 2 * p2603 = 948.0 + 216 | ||||||
|  |         #   => p2603 approx= 215.5 | ||||||
|  |         #   => x = 948. - 215. = 733.0 | ||||||
|  |         # verification : | ||||||
|  |         #   x + p2609 = 1057.0 | ||||||
|  |         #   => p2609 = 1057-733=324.0 | ||||||
|  |         #   x + 2 * p2609 = 1381.0 | ||||||
|  |         return 733.0 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 240.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeR730(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('r730', num_cpu_per_server=2, num_servers=1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # for r730 on 06/10/2016 | ||||||
|  |         #   (x: price without procs, p1 : price of e5-2603v4, p2: price of e5-2609v4) | ||||||
|  |         #   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 | ||||||
|  |         #   x + p1 = 1014.0 | ||||||
|  |         #   x + 2 * p1 = 1014.0 + 216 | ||||||
|  |         #   => p1 approx= 215.5 | ||||||
|  |         #   => x = 1014. - 215. = 799.0 | ||||||
|  |         #   x + p2 = 1123.0 | ||||||
|  |         #   => p2 = 324.0 | ||||||
|  |         #   x + 2 * p2 = 1447.0 | ||||||
|  |         return 799.0 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 240.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeC4130(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('c4130', num_cpu_per_server=2, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # for c4130 on 14/10/2016 | ||||||
|  |         # x + 2 x E5-2640v4 + 128G + 2 * K80 + X520 + p5years = 12281€ | ||||||
|  |         # x + 2 x E5-2640v4 + 128G + 4 * K80 + X520 + p5years = 19317€ | ||||||
|  |         # price of a K80 | ||||||
|  |         # >>> (19317.-12281)/2 | ||||||
|  |         # 3518.0 | ||||||
|  |         # assuming the options cost the same as for R630 (X520=210€, p5years=240€, 128G=1778€, E5-2640v4=951€), the cost of the base system is : | ||||||
|  |         # >>> 12281-951-951-1778-210-240-3518-3518 | ||||||
|  |         # 1115 | ||||||
|  |         # but if we integrate the X520 card so that we have a 10Gb ethernet in the base, the cost of the base system becomes : | ||||||
|  |         # >>> 1115+210 | ||||||
|  |         # 1325 | ||||||
|  |         return 1325.0 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 240.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeC6320(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('c6320', num_cpu_per_server=2, num_servers=4) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # for 4xc6320 on 14/10/2016 | ||||||
|  |         #   (x: price without procs, p2603: price of e5-2603v4, p2609: price of e5-2609v4) | ||||||
|  |         # x + 4 x (2 x p2620 +  p32G) =  5135 € HT | ||||||
|  |         # x + 4 x (2 x p2640 + p128G + pX520 + p5years) = 15590 € HT | ||||||
|  |         # x + 4 x (2 x p2650 + p128G + pX520 + p5years) = 17340 € HT | ||||||
|  |         # x + 4 x (2 x p2660 + p128G + pX520 + p5years) = 19490 € HT | ||||||
|  |         # by examining this and the price of processors on R630 | ||||||
|  |         # - E5-2620v4 :   458€ | ||||||
|  |         # - E5-2640v4 :   951€ | ||||||
|  |         # - E5-2650v4 :  1209€ | ||||||
|  |         # - E5-2660v4 :  1525€ | ||||||
|  |         # - E5-2680v4 :  1867€ | ||||||
|  |         # - E5-2690v4 :  2261€ | ||||||
|  |         # I could work out that : | ||||||
|  |         # - the price of procs on c6320 is the price of procs on r630 * 85% | ||||||
|  |         # - the price of the base c6320 with 32 Go and no proc at all is 2020.6 | ||||||
|  |         # - the price of the 32G to 128G upgrade is 6222.6 euros (cheaper price of 16G->128G upgrade on r630 : (1778*4 = 7112)) | ||||||
|  |         # details : | ||||||
|  |         #     >>> (19490.-17340)/8 | ||||||
|  |         #     268.75 | ||||||
|  |         #     >>> (17340.-15590)/8 | ||||||
|  |         #     218.75 | ||||||
|  |         #     >>> 218.75/258. | ||||||
|  |         #     0.8478682170542635 | ||||||
|  |         #     >>> 268.75/316 | ||||||
|  |         #     0.8504746835443038 | ||||||
|  |         #     >>> 15590.0+((1209.0-951.0)*0.85)*8 | ||||||
|  |         #     17344.4 | ||||||
|  |         #     >>> 15590.0+((1525.0-951.0)*0.85)*8 | ||||||
|  |         #     19493.2 | ||||||
|  |         #   price of 128G ram upgrade assuming that 5years guarantee costs 880€ (same as c6220), | ||||||
|  |         #     >>> 15590.0+((458.0-951.0)*0.85)*8-210.0*4-880.0 - 5135.0 | ||||||
|  |         #    6222.6 | ||||||
|  |         #     >>> 5135.0 - (458.0*0.85)*8 | ||||||
|  |         #     2020.6 | ||||||
|  |         return 2020.6 | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 880.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeR640(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('r640', num_cpu_per_server=2, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # on 29/09/2017 | ||||||
|  |         #   (x: price without procs, p3106: price of Bronze-3106, p6126: price of Gold6126) | ||||||
|  |         #   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 | ||||||
|  |         #   x + p3106 = 1067.0 | ||||||
|  |         #   x + 2 * p3106 = 1067.0 + 320.0 | ||||||
|  |         #   => p3106 = 320 | ||||||
|  |         #   => x = 1067.0 - 320.0 = 747.0 | ||||||
|  |         #     check if x computation is consistent with p6126 | ||||||
|  |         #   x + p6126 = 2767 | ||||||
|  |         #   x + 2 * p6126 = 4787.0 | ||||||
|  |         #   => p6126 = 2020.0 | ||||||
|  |         #   => x = 747.0 --> yes ! | ||||||
|  |         return 747.0 | ||||||
|  | 
 | ||||||
|  |     def get_dimm_price(self, dimm_capacity): | ||||||
|  |         return { | ||||||
|  |             8: 80.0, | ||||||
|  |             16: 160.0, | ||||||
|  |             32: 320.0, | ||||||
|  |             64: 640.0 | ||||||
|  |         }[dimm_capacity] | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         if guarantee_duration > 7: | ||||||
|  |             assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id | ||||||
|  |         elif guarantee_duration >= 5: | ||||||
|  |             return 270.0  # from dell matinfo4 online quotation | ||||||
|  |         else: | ||||||
|  |             # 5-year guarantee included in base price | ||||||
|  |             return 0.0 * self.num_servers | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         # Retrait des disques de base (2x600Go 10K SAS 2.5'') : -260.0 € | ||||||
|  |         # Ajout d'un disque dur 1,2 To SAS 10k Tpm 2,5" - hotplug : 165.0 € | ||||||
|  |         base_disks_removal_price = -260.0 | ||||||
|  |         disk_1200g_price = 165.0 | ||||||
|  |         return (base_disks_removal_price + disk_1200g_price * 2) * self.num_servers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeR940(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('r940', num_cpu_per_server=4, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # price of r940 (with 2x xeon gold 5215 and 32 Go DDR4 @ 2933GHz) on 09/06/2020 : 3784€ | ||||||
|  |         #   (x: price without procs, p5215: price of gold-5215, p6248: price of Gold6248) | ||||||
|  |         #   p6240 = 2684 | ||||||
|  |         #   p6248 = 3442 | ||||||
|  |         #   p8280l = 12075 | ||||||
|  |         #   x + 2 * p5215 = 3784 | ||||||
|  |         #   x + 4 * p6240 = 11886   =>    x = 1150 | ||||||
|  |         #   x + 4 * p6248 = 14918   =>    x = 1150 | ||||||
|  |         #   x + 4 * p8280l = 49450  =>    x = 1150 | ||||||
|  |         #   => p5215 = 1317   (agrees with proc price on r640) | ||||||
|  |         return 1150.0 | ||||||
|  | 
 | ||||||
|  |     def get_dimm_price(self, dimm_capacity): | ||||||
|  |         return { | ||||||
|  |             8: 80.0, | ||||||
|  |             16: 160.0, | ||||||
|  |             32: 320.0, | ||||||
|  |             64: 640.0 | ||||||
|  |         }[dimm_capacity] | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         if guarantee_duration > 7: | ||||||
|  |             assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id | ||||||
|  |         elif guarantee_duration >= 5: | ||||||
|  |             return 630.0  # from dell matinfo4 online quotation | ||||||
|  |         else: | ||||||
|  |             # 5-year guarantee included in base price | ||||||
|  |             return 0.0 | ||||||
|  | 
 | ||||||
|  |     @abstractmethod | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         # Retrait des disques de base (2x600Go 10K SAS 2.5'') : -260.0 € | ||||||
|  |         # Ajout d'un disque dur 1,2 To SAS 10k Tpm 2,5" - hotplug : 165.0 € | ||||||
|  |         base_disks_removal_price = -260.0 | ||||||
|  |         disk_1200g_price = 165.0 | ||||||
|  |         return (base_disks_removal_price + disk_1200g_price * 2) * self.num_servers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellPrecision3630(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__('prceision3630', num_cpu_per_server=1, num_servers=1) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         return 449.0 | ||||||
|  | 
 | ||||||
|  |     def get_dimm_price(self, dimm_capacity): | ||||||
|  |         return { | ||||||
|  |             8: 80.0, | ||||||
|  |             16: 160.0, | ||||||
|  |             32: 320.0 | ||||||
|  |         }[dimm_capacity] | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         return 0.0 | ||||||
|  | 
 | ||||||
|  | class DellPowerEdgeC6420(TableBasedConfigurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, host_type_id): | ||||||
|  |         super().__init__(host_type_id, num_cpu_per_server=2, num_servers=4) | ||||||
|  | 
 | ||||||
|  |     def get_empty_price(self): | ||||||
|  |         # for 4xc6420 on 19/06/2020 (from excel quotation) | ||||||
|  |         #  | ||||||
|  |         #  | ||||||
|  |         #   (x: price without procs, p2630: price of xeon gold 6230) | ||||||
|  |         # x + 4 x (2 x p4210r + p48g) =  5368 € HT | ||||||
|  |         # x + 4 x (2 x p6230r + p192g) = 27213 € HT | ||||||
|  |         #  | ||||||
|  |         # p48g = 3 * 160.0  # the price of a 16G ram is 160.0 € | ||||||
|  |         # p4210r = p4210r=978./2 # from r640 prices | ||||||
|  |         #  | ||||||
|  | 
 | ||||||
|  |         # >>> p4210r=978./2 | ||||||
|  |         # >>> p6230r_upgrade = 13408.0 | ||||||
|  |         # >>> p6230r_for_r640 = 2165.0 | ||||||
|  |         # >>> num_servers_per_c6000 = 4 | ||||||
|  |         # >>> num_cpu_per_server = 2 | ||||||
|  |         # >>> p6230r_for_c6420 = (p6230r_upgrade + p4210r * (num_servers_per_c6000 * num_cpu_per_server))/(num_servers_per_c6000 * num_cpu_per_server) | ||||||
|  |         # >>> p6230r_for_c6420 | ||||||
|  |         # 2165.0 | ||||||
|  | 
 | ||||||
|  |         # => p4210r seems to be the same on r640 and c6420 | ||||||
|  |         #  | ||||||
|  |         # pc6000 = 5368 - (p4210r * num_cpu_per_server + p48g) * num_servers_per_c6000 | ||||||
|  |         # >>> p16g = 160.0 | ||||||
|  |         # >>> p48g = p16g * 3 | ||||||
|  |         # >>> pc6000 = 5368 - (p4210r * num_cpu_per_server + p48g) * num_servers_per_c6000 | ||||||
|  |         # >>> pc6000 | ||||||
|  |         # -464.0 | ||||||
|  |         # >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) | ||||||
|  |         # Traceback (most recent call last): | ||||||
|  |         #   File "<stdin>", line 1, in <module> | ||||||
|  |         # NameError: name 'p192g' is not defined | ||||||
|  |         # >>> p192g = (192/16)*p16g | ||||||
|  |         # >>> p192g | ||||||
|  |         # 1920.0 | ||||||
|  |         # >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) | ||||||
|  |         # 24536.0 | ||||||
|  |         # >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) + 1159 + 68 + 350 + 1100 | ||||||
|  |         # 27213.0 | ||||||
|  |         num_servers_per_c6000 = 4 | ||||||
|  |         num_cpu_per_server = 2 | ||||||
|  |         ram_price_per_gigabyte = 160.0 / 16 # 16G ram price : 160.0 € | ||||||
|  |         xeon_silver_4210r_price = 978.0 / 2 # from r640 prices | ||||||
|  |         basic_config_price = 5368.0 | ||||||
|  |         poweredge_c6000_price = basic_config_price - (xeon_silver_4210r_price * num_cpu_per_server + ram_price_per_gigabyte * 48) * num_servers_per_c6000 | ||||||
|  |         return poweredge_c6000_price | ||||||
|  | 
 | ||||||
|  |     def get_dimm_price(self, dimm_capacity): | ||||||
|  |         return { | ||||||
|  |             8: 80.0, | ||||||
|  |             16: 160.0, | ||||||
|  |             32: 320.0, | ||||||
|  |             64: 640.0 | ||||||
|  |         }[dimm_capacity] | ||||||
|  | 
 | ||||||
|  |     def get_guarantee_price(self, guarantee_duration): | ||||||
|  |         if guarantee_duration > 7: | ||||||
|  |             assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id | ||||||
|  |         elif guarantee_duration >= 5: | ||||||
|  |             return 1100.0  # from c6420-20200716-price | ||||||
|  |         else: | ||||||
|  |             # 5-year guarantee included in base price | ||||||
|  |             return 0.0 | ||||||
|  | 
 | ||||||
|  |     def get_disk_upgrade_price(self, asked_disk_capacity): | ||||||
|  |         assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id | ||||||
|  |         # from c6420-20200716-price | ||||||
|  |         # | Ajout d'un disque dur 1 To SATA 7200 Tpm 3,5'' pour les 4 serveurs                                                                                                                                                                                                        |                                         | 4-3-1-14g096         | C6420   | 361 €          |     | € -         | | ||||||
|  |         return 361.0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DellConfiguratorParser(): | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def _get_module(cls, root_element, section_label): | ||||||
|  |         modules_element = root_element.xpath(".//div[@class='col-md-10']")[0] | ||||||
|  |         # print(modules_element) | ||||||
|  |         for module_root in modules_element.xpath(".//div[@class='col-md-12 module']"): | ||||||
|  |             # print(module_root) | ||||||
|  |             # blue modules such as "Processeurs (Passage)" | ||||||
|  |             module_titles = module_root.xpath(".//div[@class='col-md-4 module-title color-017EB8']") | ||||||
|  |             if len(module_titles) > 0: | ||||||
|  |                 # print(module_title.text) | ||||||
|  |                 # print(len(module_title.text)) | ||||||
|  |                 module_title = module_titles[0] | ||||||
|  |                 # print(module_title.text_content()) | ||||||
|  |                 if module_title.text == section_label: | ||||||
|  |                     return module_root | ||||||
|  |             # grey modules such as 'Base' | ||||||
|  |             module_titles = module_root.xpath(".//div[@class='col-md-4 module-title color-808080']") | ||||||
|  |             if len(module_titles) > 0: | ||||||
|  |                 # print(module_title.text) | ||||||
|  |                 # print(len(module_title.text)) | ||||||
|  |                 module_title = module_titles[0] | ||||||
|  |                 # print(module_title.text_content()) | ||||||
|  |                 if module_title.text == section_label: | ||||||
|  |                     return module_root | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def price_str_as_float(cls, price_as_str): | ||||||
|  |         match = re.match(r'^\s*(?P<sign>[-+]?)\s*(?P<numbers>[0-9.]*)\s*€\s*$', price_as_str) | ||||||
|  |         assert match | ||||||
|  |         # print(match['sign'], match['numbers']) | ||||||
|  |         price_as_float = float("%s%s" % (match['sign'], match['numbers'])) | ||||||
|  |         return price_as_float | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  |     def _parse_proc_change_options(self, html_root): | ||||||
|  |         proc_options = Module('processor-change') | ||||||
|  |         #module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') | ||||||
|  |         module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') | ||||||
|  |         for option_root_element in module_root_element.xpath(".//div[@class='row']"): | ||||||
|  |             label_elements = option_root_element.xpath(".//div[@class='option-not-selected ']") | ||||||
|  |             if len(label_elements) > 0: | ||||||
|  |                 label = label_elements[0].text_content().replace('\n', '') | ||||||
|  |                 price = DellConfiguratorParser.price_str_as_float(option_root_element.xpath(".//div[@class='col-md-3 text-right option-price ']")[0].text_content()) | ||||||
|  |                 # print(label, price) | ||||||
|  |                 # Passage à processeur Intel Xeon Gold 6240L 2.6GHz, 24.75M Cache,10.40GT/s, 2UPI, Turbo, HT,18C/36T (150W) - DDR4-2933 | ||||||
|  |                 match = re.match(r'^Passage à processeur Intel Xeon (?P<cpu_class>Silver|Gold|Platinium) (?P<cpu_number>[0-9][0-9][0-9][0-9][RLYU]?).*', label) | ||||||
|  |                 assert match, 'unhandled label : %s' % label | ||||||
|  |                 # print(match['cpu_class'], match['cpu_number']) | ||||||
|  |                 cpu_class = match['cpu_class'].lower() | ||||||
|  |                 if cpu_class == 'platinium': | ||||||
|  |                     cpu_class = 'platinum' | ||||||
|  |                 cpu_id = "intel-xeon-%s-%s" % (cpu_class, match['cpu_number'].lower()) | ||||||
|  |                 option = Option(Cpu(cpu_id), price) | ||||||
|  |                 proc_options.add_option(option) | ||||||
|  |         return proc_options | ||||||
|  | 
 | ||||||
|  |     def _parse_proc_options(self, html_root): | ||||||
|  |         proc_options = Module('processor') | ||||||
|  |         #module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') | ||||||
|  |         module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs  additionnels') | ||||||
|  |         for option_root_element in module_root_element.xpath(".//div[@class='row']"): | ||||||
|  |             label_elements = option_root_element.xpath(".//div[@class='option-not-selected ']") | ||||||
|  |             if len(label_elements) > 0: | ||||||
|  |                 label = label_elements[0].text_content() | ||||||
|  |                 price = DellConfiguratorParser.price_str_as_float(option_root_element.xpath(".//div[@class='col-md-3 text-right option-price ']")[0].text_content()) | ||||||
|  |                 # print(label, price) | ||||||
|  |                 match = re.match(r'^Processeur additionnel Intel Xeon (?P<cpu_class>Silver|Gold|Platinium) (?P<cpu_number>[0-9][0-9][0-9][0-9][RLY]?).*', label) | ||||||
|  |                 assert match | ||||||
|  |                 # print(match['cpu_class'], match['cpu_number']) | ||||||
|  |                 cpu_class = match['cpu_class'].lower() | ||||||
|  |                 if cpu_class == 'platinium': | ||||||
|  |                     cpu_class = 'platinum' | ||||||
|  |                 cpu_id = "intel-xeon-%s-%s" % (cpu_class, match['cpu_number'].lower()) | ||||||
|  |                 option = Option(Cpu(cpu_id), price) | ||||||
|  |                 proc_options.add_option(option) | ||||||
|  |         return proc_options | ||||||
|  | 
 | ||||||
|  |     def _parse_ram_options(self, html_root): | ||||||
|  |         ram_options = Module('ram') | ||||||
|  |         #module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') | ||||||
|  |         module_root_element = DellConfiguratorParser._get_module(html_root, 'Mémoire: Ajout de barettes additionnelles') | ||||||
|  |         for option_root_element in module_root_element.xpath(".//div[@class='row']"): | ||||||
|  |             label_elements = option_root_element.xpath(".//div[@class='option-not-selected ']") | ||||||
|  |             if len(label_elements) > 0: | ||||||
|  |                 label = label_elements[0].text_content() | ||||||
|  |                 price = DellConfiguratorParser.price_str_as_float(option_root_element.xpath(".//div[@class='col-md-3 text-right option-price ']")[0].text_content()) | ||||||
|  |                 # print(label, price) | ||||||
|  |                 # Ajout d'une barette de 128Go 2667 Mhz LRDIMM | ||||||
|  |                 match = re.match(r'^Ajout d\'une barette de (?P<num_gb>[0-9]+)Go (?P<num_mhz>[0-9][0-9][0-9][0-9]) Mhz (?P<mem_type>LRDIMM|RDIMM)$', label) | ||||||
|  |                 if match: | ||||||
|  |                     # print(match['num_gb'], match['num_mhz']) | ||||||
|  |                     dimm = Dimm(num_gb=int(match['num_gb']), num_mhz=int(match['num_mhz']), mem_type=match['mem_type'].lower()) | ||||||
|  |                     option = Option(dimm, price) | ||||||
|  |                     ram_options.add_option(option) | ||||||
|  |                 else: | ||||||
|  |                     # Optane DC Persistent Memory - 128Go 2666Mhz | ||||||
|  |                     match = re.match(r'^Optane DC Persistent Memory - (?P<num_gb>[0-9]+)Go (?P<num_mhz>[0-9][0-9][0-9][0-9])Mhz$', label) | ||||||
|  |                     if match: | ||||||
|  |                         # print(match['num_gb'], match['num_mhz']) | ||||||
|  |                         dimm = Dimm(num_gb=int(match['num_gb']), num_mhz=int(match['num_mhz']), mem_type='pmm')  # persistent memory module | ||||||
|  |                         option = Option(dimm, price) | ||||||
|  |                         ram_options.add_option(option) | ||||||
|  |                     else: | ||||||
|  |                         assert False, 'unhandled label : %s' % label | ||||||
|  |         return ram_options | ||||||
|  | 
 | ||||||
|  |     def _parse_base_config(self, html_root, configurator): | ||||||
|  |         base_config = Config(configurator) | ||||||
|  |         base_config.num_servers = configurator.chassis.item.max_num_servers | ||||||
|  |         base_config.num_cpu_per_server = 1 | ||||||
|  |         module_root_element = DellConfiguratorParser._get_module(html_root, 'Processeurs (Passage)') | ||||||
|  |         assert module_root_element is not None | ||||||
|  |         for option_root_element in module_root_element.xpath(".//div[@class='row']"): | ||||||
|  |             label_elements = option_root_element.xpath(".//div[@class='option-selected ']") | ||||||
|  |             assert label_elements is not None | ||||||
|  |             if len(label_elements) > 0: | ||||||
|  |                 label = label_elements[0].text_content().replace('\n', '') | ||||||
|  |                 price = DellConfiguratorParser.price_str_as_float(option_root_element.xpath(".//div[@class='col-md-3 text-right option-price option-price-selected']")[0].text_content()) | ||||||
|  |                 assert price == 0.0 | ||||||
|  |                 # print(label, price) | ||||||
|  |                 # Processeur Intel Xeon Silver 4208 2.1GHz,11M Cache,9.60GT/s, 2UPI,No Turbo, HT,8C/16T (85W) - DDR4-2400 | ||||||
|  |                 match = re.match(r'^Processeur Intel Xeon (?P<cpu_class>Silver|Gold|Platinium) (?P<cpu_number>[0-9][0-9][0-9][0-9][RLYU]?).*', label) | ||||||
|  |                 assert match, 'unhandled label : %s' % label | ||||||
|  |                 # print(match['cpu_class'], match['cpu_number']) | ||||||
|  |                 cpu_id = "intel-xeon-%s-%s" % (match['cpu_class'].lower(), match['cpu_number'].lower()) | ||||||
|  |                 base_config.set_cpu(Cpu(cpu_id)) | ||||||
|  |         assert base_config.cpu is not None | ||||||
|  |         return base_config | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def parse(self, dell_configurator_html_file_path, configurator): | ||||||
|  | 
 | ||||||
|  |         html_root = parse(dell_configurator_html_file_path).getroot() | ||||||
|  |         # print(dir(html_root)) | ||||||
|  |         # for e in html_root: | ||||||
|  |         #     print(e.tag) | ||||||
|  |         # body = html_root.find('body') | ||||||
|  |         # print(body) | ||||||
|  |         # for e in body: | ||||||
|  |         #     print(e.tag, e.attrib) | ||||||
|  |         # div = body.find('div') | ||||||
|  |         # for e in div: | ||||||
|  |         #     print(e.tag, e.attrib) | ||||||
|  |          | ||||||
|  |         # modules_element = body.xpath("//div[@class='col-md-10']") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         module_root_element = DellConfiguratorParser._get_module(html_root, 'Base') | ||||||
|  |         assert module_root_element is not None | ||||||
|  |         for option_root_element in module_root_element.xpath(".//div[@class='row']"): | ||||||
|  |             label_elements = option_root_element.xpath(".//div[@class='option-selected ']") | ||||||
|  |             assert len(label_elements) > 0 | ||||||
|  |             label = label_elements[0].text_content().replace('\n', '') | ||||||
|  |             # PowerEdge R640 | ||||||
|  |             match = re.match(r'^PowerEdge (?P<chassis_type>[CR][0-9][0-9][0-9][0-9]?).*', label) | ||||||
|  |             assert match, 'unhandled label : %s' % label | ||||||
|  |             # print(match['cpu_class'], match['cpu_number']) | ||||||
|  |             chassis_id = "dell-poweredge-%s" % (match['chassis_type'].lower(), ) | ||||||
|  |             configurator.chassis = Option(Chassis(chassis_id), 0.0) | ||||||
|  | 
 | ||||||
|  |         configurator.base_config = self._parse_base_config(html_root, configurator) | ||||||
|  | 
 | ||||||
|  |         # configurator.add_module(self._parse_proc_change_options(html_root)) | ||||||
|  |         configurator.add_module(self._parse_proc_options(html_root)) | ||||||
|  |         configurator.add_module(self._parse_ram_options(html_root)) | ||||||
|  | 
 | ||||||
|  |         # compute the price of the chassis | ||||||
|  |         base_price = None | ||||||
|  |         price_preview_element = html_root.xpath(".//div[@class='price-preview']")[0] | ||||||
|  |         assert price_preview_element is not None | ||||||
|  |         for price_element in price_preview_element.xpath(".//div"): | ||||||
|  |             price_label_element = price_element.xpath(".//span[@class='col-md-4']")[0] | ||||||
|  |             # <div class="price-preview"> | ||||||
|  |             #     <div class="row"><span class="col-md-4">Prix</span><span | ||||||
|  |             #             class="col-md-8">1175.00 €</span></div> | ||||||
|  |             #     <div class="row"><span class="col-md-4">Options</span><span | ||||||
|  | 
 | ||||||
|  |             #             class="col-md-8">0.00 €</span></div> | ||||||
|  |             #     <div class="row"><span class="col-md-4">Total</span><span | ||||||
|  |             #             class="col-md-8">1175.00 €</span></div> | ||||||
|  |             # </div> | ||||||
|  | 
 | ||||||
|  |             assert price_label_element is not None | ||||||
|  |             label = price_label_element.text_content().replace('\n', '') | ||||||
|  |             if label == 'Prix': | ||||||
|  |                 price_value_element = price_element.xpath(".//span[@class='col-md-8']")[0] | ||||||
|  |                 assert price_value_element is not None | ||||||
|  |                 base_price = DellConfiguratorParser.price_str_as_float(price_value_element.text_content()) | ||||||
|  |         assert base_price is not None | ||||||
|  |         configurator.chassis.price = base_price - configurator.get_item_price(configurator.base_config.cpu.uid) | ||||||
|  | 
 | ||||||
|  | class DellMatinfoConfigurator(Configurator): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, dell_configurator_html_file_path): | ||||||
|  |         super().__init__(self) | ||||||
|  |         self.base_config = None | ||||||
|  |         self.chassis = None | ||||||
|  |         parser = DellConfiguratorParser() | ||||||
|  |         parser.parse(dell_configurator_html_file_path, self) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def create_config(self): | ||||||
|  |         return copy.copy(self.base_config) | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,242 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | import numpy | ||||||
|  | import pylab | ||||||
|  | import matplotlib.pyplot as plt | ||||||
|  | import matplotlib.colors | ||||||
|  | import itertools | ||||||
|  | import re | ||||||
|  | import hashlib | ||||||
|  | from string import ascii_lowercase | ||||||
|  | from concho.config import Configurator | ||||||
|  | from concho.config import Config | ||||||
|  | from concho.config import Cpu | ||||||
|  | from concho import dell | ||||||
|  | 
 | ||||||
|  | markerTypes=[',', '+', '.', '^', 'v', '<', '>', 'o', '*', '1', '2', '3', '4', '8', 's', 'p', 'h', 'H', 'x', 'X', 'D', 'd', '|', '_'] | ||||||
|  | #for c in ascii_lowercase: | ||||||
|  | #    markerTypes.append('$%s$' % c) | ||||||
|  | #markerColors=('r', 'g', 'b') | ||||||
|  | markerColors=('r') | ||||||
|  | 
 | ||||||
|  | def get_marker(proc_id): | ||||||
|  |     hash_object = hashlib.md5(proc_id.encode('utf-8')) | ||||||
|  |     hash = int(hash_object.hexdigest(), 16) | ||||||
|  |     return markerTypes[ hash % len(markerTypes) ] | ||||||
|  | 
 | ||||||
|  | def plotCpuPassmark(): | ||||||
|  |     cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U10", float, int, float, float), names=True, delimiter='\t') | ||||||
|  |     plt.subplot(1,1,0) | ||||||
|  |     plt.subplots_adjust(bottom = 0.1) | ||||||
|  |     markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) | ||||||
|  |     labels = cpuTable['id'] | ||||||
|  |     x = cpuTable['clock'] * cpuTable['num_cores'] | ||||||
|  |     y = cpuTable['cpumark'] | ||||||
|  |     markerSize = 50 | ||||||
|  |     color = 'b' | ||||||
|  |     for label, x1, y1 in zip(labels, x, y): | ||||||
|  |         if  y1 <= 0.0: | ||||||
|  |             continue # no passmark available fo this data | ||||||
|  |         generation=label[-1] | ||||||
|  |         if generation == '2': | ||||||
|  |             color = 'b' | ||||||
|  |         else: | ||||||
|  |             color = 'r'             | ||||||
|  |         marker = markersCycler.next() | ||||||
|  |         plt.scatter( x1, y1, color = color, s = markerSize, marker = marker[0], label = label)     | ||||||
|  |     plt.xlabel(u'theoretical cpu speed [core.GHz]') | ||||||
|  |     plt.ylabel(u'passmark [?]') | ||||||
|  |     plt.title(u'comparison between cpu theoretical and effective speed') | ||||||
|  |     plt.xlim( xmin = 0.0 ) | ||||||
|  |     plt.ylim( ymin = 0.0 ) | ||||||
|  |     plt.legend(bbox_to_anchor=(0.2, 1.0)) | ||||||
|  |     #plt.legend() | ||||||
|  |     plt.draw() | ||||||
|  |     plt.show() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def plot_system_efficiency(): | ||||||
|  | 
 | ||||||
|  |     cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U15", float, int, float, float, float), names=True, delimiter='\t') | ||||||
|  |     #cpuTable = numpy.genfromtxt('dell_ivybridge_table.dat', dtype=(('id', "|S10"), ('clock', float), ('num_cores', int), ('price', float, float)), names=None, delimiter='\t') | ||||||
|  |     print(type(cpuTable)) | ||||||
|  |     print(cpuTable.dtype) | ||||||
|  |     print(cpuTable) | ||||||
|  |     print(cpuTable['id']) | ||||||
|  | 
 | ||||||
|  |     #cpuTable = numpy.genfromtxt('dell_ivybridge_table.dat', dtype=(('id', "|S10"), ('clock', float), ('num_cores', int), ('price', float, float)), names=None, delimiter='\t') | ||||||
|  | 
 | ||||||
|  |     #for (x, y) in clusters: | ||||||
|  |     def GHzToMHz( frequency ): | ||||||
|  |         return frequency * 1000.0 | ||||||
|  |          | ||||||
|  |     kWHPrice = 0.07 * 1.5 | ||||||
|  |     containerLifetime = 7.0 # in years | ||||||
|  |     powerUsageEfficiency = 0.5 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def getColorCodeFromItemLabel(label): | ||||||
|  |         generation=label[-1] | ||||||
|  |         (model, proc_id) = re.split('_', label) | ||||||
|  |         saturation = { | ||||||
|  |             'sandy bridge':0.0, | ||||||
|  |             'ivy bridge':0.2, | ||||||
|  |             'haswell':0.2, | ||||||
|  |             'broadwell':0.2, | ||||||
|  |             'skylake':0.4, | ||||||
|  |             'coffeelake':0.6, | ||||||
|  |             'cascadelake':1.0 | ||||||
|  |             }[Cpu(proc_id).architecture] | ||||||
|  |             #         if model == 'r620': | ||||||
|  |             #             color = 'r' | ||||||
|  |             #         elif model == 'r630': | ||||||
|  |             #             color = 'g' | ||||||
|  |             #         elif model == 'r730': | ||||||
|  |             #             color = 'm' | ||||||
|  |             #         elif model == 'c6220': | ||||||
|  |             #             if generation == '2': | ||||||
|  |             #                 color = 'b' | ||||||
|  |             #             else: | ||||||
|  |             #                 color = 'y' | ||||||
|  |         hue = { | ||||||
|  |             'dell-poweredge-r620': 0.6, | ||||||
|  |             'dell-poweredge-r630': 0.6, | ||||||
|  |             'dell-poweredge-r640': 0.6, | ||||||
|  |             'dell-poweredge-c4310': 0.6, | ||||||
|  |             'dell-poweredge-r730': 0.4, | ||||||
|  |             'dell-poweredge-r940': 0.8, | ||||||
|  |             'dell-poweredge-c6220': 1.0, | ||||||
|  |             'dell-poweredge-c6320': 1.0, | ||||||
|  |             'dell-poweredge-c6420': 1.0, | ||||||
|  |             'dell-precision-3630': 0.2 | ||||||
|  |             }[model] | ||||||
|  |         value = 0.9 | ||||||
|  |         return matplotlib.colors.hsv_to_rgb((hue, saturation, value)) | ||||||
|  | 
 | ||||||
|  |     def get_marker_from_label(label): | ||||||
|  |         (model, proc_id) = re.split('_', label) | ||||||
|  |         return get_marker(proc_id) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     item_price = numpy.array([]) | ||||||
|  |     item_power_consumption = numpy.array([]) | ||||||
|  |     item_speed = numpy.array([]) | ||||||
|  |     item_label = numpy.array([]) | ||||||
|  |     configurators = [ | ||||||
|  |         dell.DellMatinfoConfigurator('rcrc1406676-4834664 - Cat2 Conf4 PowerEdge R640 - Dell.html')] | ||||||
|  |         # dell.DellPowerEdgeR940()] | ||||||
|  |     for configurator in configurators: | ||||||
|  |         config = configurator.create_config() | ||||||
|  |         for cpu in configurator.get_cpu_options(): | ||||||
|  |             if not cpu.architecture in ['coffeelake', 'skylake','cascadelake']: | ||||||
|  |                 continue | ||||||
|  |             item_label = numpy.append( item_label, config.chassis.uid + '_' + cpu.uid ) | ||||||
|  |             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 | ||||||
|  |             # print('procOptionPrice', procOptionPrice) | ||||||
|  |             # config_price = procOptionPrice | ||||||
|  |             # config_price += config.get_empty_price() | ||||||
|  |             # print('config.get_empty_price()', config.get_empty_price()) | ||||||
|  |             # ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_core=4.0e9) | ||||||
|  |             # # ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_server=192.0e9) | ||||||
|  |             # # ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_cpu=96.0e9) | ||||||
|  |             # print('ram_update_price', ram_update_price) | ||||||
|  |             # config_price += ram_update_price | ||||||
|  |             # config_price += config.get_guarantee_price(5) | ||||||
|  |             # print('config.config.get_guarantee_price(5)', config.get_guarantee_price(5)) | ||||||
|  |             # config_price += config.get_disk_upgrade_price(2.0e12) | ||||||
|  |             # print('config.get_disk_upgrade_price(2.0e12)', config.get_disk_upgrade_price(2.0e12)) | ||||||
|  |             item_price = numpy.append( item_price, config.get_price() ) | ||||||
|  |             item_power_consumption = numpy.append( item_power_consumption, config.get_power_consumption()) | ||||||
|  |             # # print(hostTypeId, procId, item_power_consumption[-1]) | ||||||
|  |             item_speed = numpy.append( item_speed, config.get_flops()) | ||||||
|  | 
 | ||||||
|  |     #pylab.plot(x, y, '+') | ||||||
|  |     #pylab.xlabel('speed/price ratio [core.MHz/euros]') | ||||||
|  |     #pylab.ylabel('speed/power consumption ratio [core.MHz/W]') | ||||||
|  |     #pylab.show() # or savefig(<filename>) | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |     #print("items = ") | ||||||
|  |     #print(item_label) | ||||||
|  | 
 | ||||||
|  |     markerSize = 50 | ||||||
|  | 
 | ||||||
|  |     if False: | ||||||
|  |         plt.subplot(1,2,1) | ||||||
|  |         plt.subplots_adjust(bottom = 0.1) | ||||||
|  |         markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) | ||||||
|  |         x = item_speed / item_price | ||||||
|  |         y = item_speed / item_power_consumption | ||||||
|  |         for label, x1, y1, power, speed, price, in zip(item_label, x, y, item_power_consumption, item_speed, item_price): | ||||||
|  |             marker = markersCycler.next() | ||||||
|  |             color = getColorCodeFromItemLabel(label) | ||||||
|  |             plt.scatter( x1, y1, color = color, s = markerSize, marker = marker[0], label = label) | ||||||
|  |             #print(x1, y1, color, markerSize, marker[0], label) | ||||||
|  |         if False: | ||||||
|  |             plt.scatter( x, y, marker = 'o') | ||||||
|  |             for label, x1, y1, power, speed, price, in zip(item_label, x, y, item_power_consumption, item_speed, item_price): | ||||||
|  |                 #print(label) | ||||||
|  |                 plt.annotate( u'%s (%.1f core.GHz, %.0f W, %.0f €)' % (label,speed/1000.0, power, price),  | ||||||
|  |                     xy = (x1, y1), xytext = (-50, 50), | ||||||
|  |                     textcoords = 'offset points', ha = 'right', va = 'bottom', | ||||||
|  |                     bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), | ||||||
|  |                     arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) | ||||||
|  |         plt.xlabel(u'speed/price ratio [core.MHz/€]') | ||||||
|  |         plt.ylabel(u'speed/power consumption ratio [core.MHz/W]') | ||||||
|  |         plt.xlim( xmin = 0.0 ) | ||||||
|  |         plt.ylim( ymin = 0.0 ) | ||||||
|  | 
 | ||||||
|  |     plt.subplot(1,2,1) | ||||||
|  |     #fig = plt.figure() | ||||||
|  |     #ax = fig.gca() | ||||||
|  |     #ax.set_xticks(numpy.arange(0,1,0.1)) | ||||||
|  |     #ax.set_yticks(numpy.arange(0,1.,0.1)) | ||||||
|  | 
 | ||||||
|  |     powerUsedInLifetime = (item_power_consumption * containerLifetime * 365 * 24) / powerUsageEfficiency | ||||||
|  |     itemTotalCost = item_price + (powerUsedInLifetime / 1000.0 * kWHPrice ) | ||||||
|  |     markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) | ||||||
|  |     item_flops = item_speed | ||||||
|  |     # print item_flops | ||||||
|  |     item_total_num_ops = item_flops * containerLifetime * 365 * 24 * 3600 | ||||||
|  |     print(item_price) | ||||||
|  |     x = item_price | ||||||
|  |     y = item_total_num_ops / itemTotalCost | ||||||
|  |     for i in range(len(item_label)): | ||||||
|  |         print(item_label[i], item_price[i], y[i]) | ||||||
|  |         print('itemTotalCost', itemTotalCost[i]) | ||||||
|  |         print('flops', item_flops[i]) | ||||||
|  |     # print y | ||||||
|  |     for label, x1, y1, power, speed, price, in zip(item_label, x, y, item_power_consumption, item_speed, item_price): | ||||||
|  |         print(label, x1, y1) | ||||||
|  |         if y1 > 0.0001: | ||||||
|  |             color = getColorCodeFromItemLabel(label) | ||||||
|  |             # marker = markersCycler.next() | ||||||
|  |             marker = get_marker_from_label( label ) | ||||||
|  |             #print(x1, y1) | ||||||
|  |             short_label = label.replace('dell-poweredge-','').replace('intel-xeon-','') | ||||||
|  |             plt.scatter( x1, y1, facecolors=color, s=markerSize, marker=marker[0], label=short_label) | ||||||
|  |             if y1 > 5.0e16: | ||||||
|  |                 plt.annotate( u'%s' % short_label,  | ||||||
|  |                     xy = (x1, y1), xytext = (x1*4.0, (y1-5.2e16)*7.1), | ||||||
|  |                     textcoords = 'data', ha = 'right', va = 'bottom', | ||||||
|  |                     bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), | ||||||
|  |                     arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) | ||||||
|  | 
 | ||||||
|  |     plt.xlabel(u'purchase price [€]') | ||||||
|  |     plt.ylabel(u'num total DP operations/total cost [€/^-1]') | ||||||
|  |     plt.title(u'total cost including electricity') | ||||||
|  |     plt.xlim( xmin = 0.0 ) | ||||||
|  |     plt.ylim( ymin = 0.0 ) | ||||||
|  |     plt.minorticks_on() | ||||||
|  |     plt.grid(b=True, which='major', color='b', linestyle='-', linewidth=0.5) | ||||||
|  |     plt.grid(b=True, which='minor', color='b', linestyle='-', linewidth=0.2)     | ||||||
|  |     plt.legend(bbox_to_anchor=(1.1, 1.1), ncol=3) | ||||||
|  |     plt.draw() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     plt.show() | ||||||
|  | 
 | ||||||
|  | #plotCpuPassmark(): | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     plot_system_efficiency() | ||||||
							
								
								
									
										309
									
								
								cpu_table.dat
								
								
								
								
							
							
						
						
									
										309
									
								
								cpu_table.dat
								
								
								
								
							|  | @ -1,158 +1,157 @@ | ||||||
| #id	clock	num_cores	tdp	cpumark_1_cpu	cpumark_2_cpu | #id	clock	num_cores	tdp	cpumark_1_cpu	cpumark_2_cpu | ||||||
| core-i7-8700k	3.7	6	95	0	0 | intel-core-i7-8700k	3.7	6	95	0	0 | ||||||
| 5150	2.66	2	65	1746	3498 | intel-xeon-5150	2.66	2	65	1746	3498 | ||||||
| E5462	2.8	4	80	4038	7350 | intel-xeon-e5462	2.8	4	80	4038	7350 | ||||||
| X5550	2.67	4	95	5416	9131 | intel-xeon-x5550	2.67	4	95	5416	9131 | ||||||
| X5560	2.8	4	95	5426	9215 | intel-xeon-x5560	2.8	4	95	5426	9215 | ||||||
| X5650	2.67	6	95	7601	11728 | intel-xeon-x5650	2.67	6	95	7601	11728 | ||||||
| X5660	2.8	6	95	7954	11893 | intel-xeon-x5660	2.8	6	95	7954	11893 | ||||||
| E5-2660	2.2	8	95	11535	17282 | intel-xeon-e5-2660	2.2	8	95	11535	17282 | ||||||
| E5-2670	2.6	8	115	0	18509 | intel-xeon-e5-2670	2.6	8	115	0	18509 | ||||||
| E5-2680	2.7	8	130	0	18480 | intel-xeon-e5-2680	2.7	8	130	0	18480 | ||||||
| E5-2690	2.9	8	135	0	20699 | intel-xeon-e5-2690	2.9	8	135	0	20699 | ||||||
| E5-2620v2	2.1	6	95	8664	13474 | intel-xeon-e5-2620v2	2.1	6	95	8664	13474 | ||||||
| E5-2630v2	2.6	6	80	10615	16256 | intel-xeon-e5-2630v2	2.6	6	80	10615	16256 | ||||||
| E5-2640v2	2	8	95	10132	14792 | intel-xeon-e5-2640v2	2	8	95	10132	14792 | ||||||
| E5-2650v2	2.6	8	95	13276	19333 | intel-xeon-e5-2650v2	2.6	8	95	13276	19333 | ||||||
| E5-2660v2	2.2	10	95	13659	18670 | intel-xeon-e5-2660v2	2.2	10	95	13659	18670 | ||||||
| E5-2670v2	2.5	10	115	14892	22062 | intel-xeon-e5-2670v2	2.5	10	115	14892	22062 | ||||||
| E5-2680v2	2.8	10	115	16340	23263 | intel-xeon-e5-2680v2	2.8	10	115	16340	23263 | ||||||
| E5-2690v2	3	10	130	17304	24189 | intel-xeon-e5-2690v2	3	10	130	17304	24189 | ||||||
| E5-2695v2	2.4	12	130	17231	21980 | intel-xeon-e5-2695v2	2.4	12	130	17231	21980 | ||||||
| E5-2697v2	2.7	12	130	17516	23910 | intel-xeon-e5-2697v2	2.7	12	130	17516	23910 | ||||||
| E5-2603v3	1.6	6	85	4996	8358 | intel-xeon-e5-2603v3	1.6	6	85	4996	8358 | ||||||
| E5-2609v3	1.9	6	85	5878	9885 | intel-xeon-e5-2609v3	1.9	6	85	5878	9885 | ||||||
| E5-2620v3	2.4	6	85	9955	15400 | intel-xeon-e5-2620v3	2.4	6	85	9955	15400 | ||||||
| E5-2623v3	3	6	105	9007	13914 | intel-xeon-e5-2623v3	3	6	105	9007	13914 | ||||||
| E5-2630v3	2.4	8	85	12803	18699 | intel-xeon-e5-2630v3	2.4	8	85	12803	18699 | ||||||
| E5-2630Lv3	1.8	8	55	11201	0 | intel-xeon-e5-2630lv3	1.8	8	55	11201	0 | ||||||
| E5-2637v3	3.5	4	135	10278	16823 | intel-xeon-e5-2637v3	3.5	4	135	10278	16823 | ||||||
| E5-2640v3	2.6	8	90	14081	20824 | intel-xeon-e5-2640v3	2.6	8	90	14081	20824 | ||||||
| E5-2643v3	3.4	6	135	13671	20574 | intel-xeon-e5-2643v3	3.4	6	135	13671	20574 | ||||||
| E5-2650v3	2.3	10	105	15106	20602 | intel-xeon-e5-2650v3	2.3	10	105	15106	20602 | ||||||
| E5-2650Lv3	1.8	12	65	13132	0 | intel-xeon-e5-2650lv3	1.8	12	65	13132	0 | ||||||
| E5-2660v3	2.6	10	105	16161	23388 | intel-xeon-e5-2660v3	2.6	10	105	16161	23388 | ||||||
| E5-2667v3	3.2	8	135	16125	22935 | intel-xeon-e5-2667v3	3.2	8	135	16125	22935 | ||||||
| E5-2670v3	2.3	12	120	16549	22330 | intel-xeon-e5-2670v3	2.3	12	120	16549	22330 | ||||||
| E5-2680v3	2.5	12	120	18840	25352 | intel-xeon-e5-2680v3	2.5	12	120	18840	25352 | ||||||
| E5-2683v3	2	14	120	17917	22704 | intel-xeon-e5-2683v3	2	14	120	17917	22704 | ||||||
| E5-2687Wv3	3.1	10	160	17785	24769 | intel-xeon-e5-2687wv3	3.1	10	160	17785	24769 | ||||||
| E5-2690v3	2.6	12	160	19567	26665 | intel-xeon-e5-2690v3	2.6	12	160	19567	26665 | ||||||
| E5-2695v3	2.3	14	120	20742	26021 | intel-xeon-e5-2695v3	2.3	14	120	20742	26021 | ||||||
| E5-2697v3	2.6	14	145	21667	29009 | intel-xeon-e5-2697v3	2.6	14	145	21667	29009 | ||||||
| E5-2698v3	2.3	16	135	21794	30217 | intel-xeon-e5-2698v3	2.3	16	135	21794	30217 | ||||||
| E5-2699v3	2.3	18	145	22520	24820 | intel-xeon-e5-2699v3	2.3	18	145	22520	24820 | ||||||
| E5-2603v4	1.7	6	85	5247	8809 | intel-xeon-e5-2603v4	1.7	6	85	5247	8809 | ||||||
| E5-2609v4	1.7	8	85	0	10835 | intel-xeon-e5-2609v4	1.7	8	85	0	10835 | ||||||
| E5-2620v4	2.1	8	85	11219	17063 | intel-xeon-e5-2620v4	2.1	8	85	11219	17063 | ||||||
| E5-2623v4	2.6	4	85	0	10196 | intel-xeon-e5-2623v4	2.6	4	85	0	10196 | ||||||
| E5-2630v4	2.2	10	85	14221	18281 | intel-xeon-e5-2630v4	2.2	10	85	14221	18281 | ||||||
| E5-2630Lv4	1.8	10	55	0	0 | intel-xeon-e5-2630lv4	1.8	10	55	0	0 | ||||||
| E5-2637v4	3.5	4	135	9665	17398 | intel-xeon-e5-2637v4	3.5	4	135	9665	17398 | ||||||
| E5-2640v4	2.4	10	90	15244	21556 | intel-xeon-e5-2640v4	2.4	10	90	15244	21556 | ||||||
| E5-2643v4	3.4	6	135	14329	22063 | intel-xeon-e5-2643v4	3.4	6	135	14329	22063 | ||||||
| E5-2650v4	2.2	12	105	16212	22619 | intel-xeon-e5-2650v4	2.2	12	105	16212	22619 | ||||||
| E5-2650Lv4	1.7	14	65	0	0 | intel-xeon-e5-2650lv4	1.7	14	65	0	0 | ||||||
| E5-2660v4	2.0	14	105	0	0 | intel-xeon-e5-2660v4	2.0	14	105	0	0 | ||||||
| E5-2667v4	3.2	8	135	0	0 | intel-xeon-e5-2667v4	3.2	8	135	0	0 | ||||||
| E5-2680v4	2.4	14	120	20489	0 | intel-xeon-e5-2680v4	2.4	14	120	20489	0 | ||||||
| E5-2683v4	2.1	16	120	0	0 | intel-xeon-e5-2683v4	2.1	16	120	0	0 | ||||||
| E5-2687Wv4	3.0	12	160	20340	27161 | intel-xeon-e5-2687wv4	3.0	12	160	20340	27161 | ||||||
| E5-2690v4	2.6	14	120	22843	28262 | intel-xeon-e5-2690v4	2.6	14	120	22843	28262 | ||||||
| E5-2695v4	2.1	18	135	19351	20768 | intel-xeon-e5-2695v4	2.1	18	135	19351	20768 | ||||||
| E5-2697v4	2.3	18	135	23070	0 | intel-xeon-e5-2697v4	2.3	18	135	23070	0 | ||||||
| E5-2697Av4	2.6	16	145	0	24075 | intel-xeon-e5-2697av4	2.6	16	145	0	24075 | ||||||
| E5-2698v4	2.2	20	135	24615	32248 | intel-xeon-e5-2698v4	2.2	20	135	24615	32248 | ||||||
| E5-2699v4	2.2	22	145	21277	38461 | intel-xeon-e5-2699v4	2.2	22	145	21277	38461 | ||||||
| Gold-5115	2.4	10 	85	0	0 | intel-xeon-gold-5115	2.4	10 	85	0	0 | ||||||
| Gold-5117	2.0	14 	105	0	0 | intel-xeon-gold-5117	2.0	14 	105	0	0 | ||||||
| Gold-5117F	2.0	14 	113	0	0 | intel-xeon-gold-5117f	2.0	14 	113	0	0 | ||||||
| Gold-5117M	2.0	0 	0	0	21250 | intel-xeon-gold-5117m	2.0	0 	0	0	21250 | ||||||
| Gold-5118	2.3	12 	105	0	0 | intel-xeon-gold-5118	2.3	12 	105	0	0 | ||||||
| Gold-5119T	1.9	14 	85	0	0 | intel-xeon-gold-5119t	1.9	14 	85	0	0 | ||||||
| Gold-5120	2.2	14 	105	0	0 | intel-xeon-gold-5120	2.2	14 	105	0	0 | ||||||
| Gold-5120T	2.2	14 	105	0	0 | intel-xeon-gold-5120t	2.2	14 	105	0	0 | ||||||
| Gold-5122	3.6	4 	105	0	0 | intel-xeon-gold-5122	3.6	4 	105	0	0 | ||||||
| Gold-6126	2.6	12 	125	0	0 | intel-xeon-gold-6126	2.6	12 	125	0	0 | ||||||
| Gold-6126F	2.6	12 	135	0	0 | intel-xeon-gold-6126f	2.6	12 	135	0	0 | ||||||
| Gold-6128	3.4	6 	115	0	0 | intel-xeon-gold-6128	3.4	6 	115	0	0 | ||||||
| Gold-6130	2.1	16 	125	0	0 | intel-xeon-gold-6130	2.1	16 	125	0	0 | ||||||
| Gold-6130F	2.1	16 	125	0	0 | intel-xeon-gold-6130f	2.1	16 	125	0	0 | ||||||
| Gold-6130T	2.1	16 	125	0	0 | intel-xeon-gold-6130t	2.1	16 	125	0	0 | ||||||
| Gold-6132	2.6	14 	140	0	0 | intel-xeon-gold-6132	2.6	14 	140	0	0 | ||||||
| Gold-6134	3.2	8 	130	0	0 | intel-xeon-gold-6134	3.2	8 	130	0	0 | ||||||
| Gold-6134M	3.2	8 	130	0	0 | intel-xeon-gold-6134m	3.2	8 	130	0	0 | ||||||
| Gold-6136	3.0	12 	150	0	0 | intel-xeon-gold-6136	3.0	12 	150	0	0 | ||||||
| Gold-6138	2.0	20 	125	0	0 | intel-xeon-gold-6138	2.0	20 	125	0	0 | ||||||
| Gold-6138F	2.0	20 	135	0	0 | intel-xeon-gold-6138f	2.0	20 	135	0	0 | ||||||
| Gold-6138T	2.0	20 	125	0	0 | intel-xeon-gold-6138t	2.0	20 	125	0	0 | ||||||
| Gold-6140	2.3	18 	140	0	0 | intel-xeon-gold-6140	2.3	18 	140	0	0 | ||||||
| Gold-6140M	2.3	18 	140	0	0 | intel-xeon-gold-6140m	2.3	18 	140	0	0 | ||||||
| Gold-6142	2.6	16 	150	0	0 | intel-xeon-gold-6142	2.6	16 	150	0	0 | ||||||
| Gold-6142F	2.6	16 	160	0	0 | intel-xeon-gold-6142f	2.6	16 	160	0	0 | ||||||
| Gold-6142M	2.6	16 	150	0	0 | intel-xeon-gold-6142m	2.6	16 	150	0	0 | ||||||
| Gold-6144	3.5	8 	150	0	0 | intel-xeon-gold-6144	3.5	8 	150	0	0 | ||||||
| Gold-6145	2.0	20 	145	0	0 | intel-xeon-gold-6145	2.0	20 	145	0	0 | ||||||
| Gold-6146	3.2	12 	165	0	0 | intel-xeon-gold-6146	3.2	12 	165	0	0 | ||||||
| Gold-6148	2.4	20 	150	0	0 | intel-xeon-gold-6148	2.4	20 	150	0	0 | ||||||
| Gold-6150	2.7	18 	165	0	26349 | intel-xeon-gold-6150	2.7	18 	165	0	26349 | ||||||
| Gold-6152	2.1	22 	140	0	0 | intel-xeon-gold-6152	2.1	22 	140	0	0 | ||||||
| Gold-6154	3.0	18 	200	0	0 | intel-xeon-gold-6154	3.0	18 	200	0	0 | ||||||
| Gold-6161	2.2	22 	165	0	0 | intel-xeon-gold-6161	2.2	22 	165	0	0 | ||||||
| 
 | 
 | ||||||
| Silver-4208	2.1	8 	85	0	0 | intel-xeon-silver-4208	2.1	8 	85	0	0 | ||||||
| Silver-4210R	2.4	10 	100	0	0 | intel-xeon-silver-4210r	2.4	10 	100	0	0 | ||||||
| Silver-4214R	2.4	12 	100	0	0 | intel-xeon-silver-4214r	2.4	12 	100	0	0 | ||||||
| Silver-4214Y	2.2	12 	85	0	0 | intel-xeon-silver-4214y	2.2	12 	85	0	0 | ||||||
| Silver-4215R	3.2	8 	130	0	0 | intel-xeon-silver-4215r	3.2	8 	130	0	0 | ||||||
| Silver-4216	2.1	16 	100	0	0 | intel-xeon-silver-4216	2.1	16 	100	0	0 | ||||||
| Gold-5215	2.5	10 	85	0	0 | intel-xeon-gold-5215	2.5	10 	85	0	0 | ||||||
| Gold-5215L	2.5	10 	85	0	0 | intel-xeon-gold-5215l	2.5	10 	85	0	0 | ||||||
| Gold-5217	3.0	8 	115	0	0 | intel-xeon-gold-5217	3.0	8 	115	0	0 | ||||||
| Gold-5218	2.3	16 	125	0	0 | intel-xeon-gold-5218	2.3	16 	125	0	0 | ||||||
| Gold-5218R	2.1	20 	125	0	0 | intel-xeon-gold-5218r	2.1	20 	125	0	0 | ||||||
| Gold-5220	2.2	18 	125	0	0 | intel-xeon-gold-5220	2.2	18 	125	0	0 | ||||||
| Gold-5220R	2.2	24 	150	0	0 | intel-xeon-gold-5220r	2.2	24 	150	0	0 | ||||||
| Gold-5222	3.8	4 	105	0	0 | intel-xeon-gold-5222	3.8	4 	105	0	0 | ||||||
| Gold-6210U	2.5	20 	150	0	0 | intel-xeon-gold-6210u	2.5	20 	150	0	0 | ||||||
| Gold-6212U	2.4	24 	165	0	0 | intel-xeon-gold-6212u	2.4	24 	165	0	0 | ||||||
| Gold-6226	2.7	12 	125	0	0 | intel-xeon-gold-6226	2.7	12 	125	0	0 | ||||||
| Gold-6226R	2.9	16 	150	0	0 | intel-xeon-gold-6226r	2.9	16 	150	0	0 | ||||||
| Gold-6230	2.1	20 	125	0	0 | intel-xeon-gold-6230	2.1	20 	125	0	0 | ||||||
| Gold-6230R	2.1	26 	150	0	0 | intel-xeon-gold-6230r	2.1	26 	150	0	0 | ||||||
| Gold-6234	3.3	8 	130	0	0 | intel-xeon-gold-6234	3.3	8 	130	0	0 | ||||||
| Gold-6238	2.1	22 	140	0	0 | intel-xeon-gold-6238	2.1	22 	140	0	0 | ||||||
| Gold-6238L	2.1	22 	140	0	0 | intel-xeon-gold-6238l	2.1	22 	140	0	0 | ||||||
| Gold-6238R	2.2	28 	165	0	0 | intel-xeon-gold-6238r	2.2	28 	165	0	0 | ||||||
| Gold-6240	2.6	18 	150	0	0 | intel-xeon-gold-6240	2.6	18 	150	0	0 | ||||||
| Gold-6240L	2.6	18 	150	0	0 | intel-xeon-gold-6240l	2.6	18 	150	0	0 | ||||||
| Gold-6240R	2.4	24 	165	0	0 | intel-xeon-gold-6240r	2.4	24 	165	0	0 | ||||||
| Gold-6240Y	2.6	18 	150	0	0 | intel-xeon-gold-6240y	2.6	18 	150	0	0 | ||||||
| Gold-6242	2.8	16 	150	0	0 | intel-xeon-gold-6242	2.8	16 	150	0	0 | ||||||
| Gold-6242R	3.1	20 	205	0	0 | intel-xeon-gold-6242r	3.1	20 	205	0	0 | ||||||
| Gold-6244	3.6	8 	150	0	0 | intel-xeon-gold-6244	3.6	8 	150	0	0 | ||||||
| Gold-6246	3.3	12 	165	0	0 | intel-xeon-gold-6246	3.3	12 	165	0	0 | ||||||
| Gold-6246R	3.4	16 	205	0	0 | intel-xeon-gold-6246r	3.4	16 	205	0	0 | ||||||
| Gold-6248	2.5	20 	150	0	0 | intel-xeon-gold-6248	2.5	20 	150	0	0 | ||||||
| Gold-6248R	3.0	24 	205	0	0 | intel-xeon-gold-6248r	3.0	24 	205	0	0 | ||||||
| Gold-6252	2.1	24 	150	0	0 | intel-xeon-gold-6252	2.1	24 	150	0	0 | ||||||
| Gold-6254	3.1	18 	200	0	0 | intel-xeon-gold-6254	3.1	18 	200	0	0 | ||||||
| Gold-6258R	2.7	28 	205	0	0 | intel-xeon-gold-6258r	2.7	28 	205	0	0 | ||||||
| Platinum-8256	3.8	4 	105	0	0 | intel-xeon-platinum-8256	3.8	4 	105	0	0 | ||||||
| Platinum-8260	2.4	24 	165	0	0 | intel-xeon-platinum-8260	2.4	24 	165	0	0 | ||||||
| Platinum-8260L	2.4	24 	165	0	0 | intel-xeon-platinum-8260l	2.4	24 	165	0	0 | ||||||
| Platinum-8260Y	2.4	24 	165	0	0 | intel-xeon-platinum-8260y	2.4	24 	165	0	0 | ||||||
| Platinum-8253	2.2	16 	125	0	0 | intel-xeon-platinum-8253	2.2	16 	125	0	0 | ||||||
| Platinum-8268	2.9	24 	205	0	0 | intel-xeon-platinum-8268	2.9	24 	205	0	0 | ||||||
| Platinum-8270	2.7	26 	205	0	0 | intel-xeon-platinum-8270	2.7	26 	205	0	0 | ||||||
| Platinum-8276	2.2	28 	165	0	0 | intel-xeon-platinum-8276	2.2	28 	165	0	0 | ||||||
| Platinum-8280	2.7	28 	205	0	0 | intel-xeon-platinum-8280	2.7	28 	205	0	0 | ||||||
| Platinum-8280L	2.7	28 	205	0	0 | intel-xeon-platinum-8280l	2.7	28 	205	0	0 | ||||||
| 
 |  | ||||||
| Platinum-8153	2.0	16	125	0	0 |  | ||||||
| Platinum-8160	2.1	24	150	0	0 |  | ||||||
| Platinum-8164	2.0	26	165	0	0 |  | ||||||
| Platinum-8168	2.7	24	205	0	0 |  | ||||||
| Platinum-8170	2.1	26	165	0	0 |  | ||||||
| Platinum-8176	2.1	28	165	0	0 |  | ||||||
| 
 | 
 | ||||||
|  | intel-xeon-platinum-8153	2.0	16	125	0	0 | ||||||
|  | intel-xeon-platinum-8160	2.1	24	150	0	0 | ||||||
|  | intel-xeon-platinum-8164	2.0	26	165	0	0 | ||||||
|  | intel-xeon-platinum-8168	2.7	24	205	0	0 | ||||||
|  | intel-xeon-platinum-8170	2.1	26	165	0	0 | ||||||
|  | intel-xeon-platinum-8176	2.1	28	165	0	0 | ||||||
|  |  | ||||||
							
								
								
									
										863
									
								
								procs_chooser.py
								
								
								
								
							
							
						
						
									
										863
									
								
								procs_chooser.py
								
								
								
								
							|  | @ -1,863 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| import numpy |  | ||||||
| import pylab |  | ||||||
| import matplotlib.pyplot as plt |  | ||||||
| import matplotlib.colors |  | ||||||
| import itertools |  | ||||||
| import re |  | ||||||
| import hashlib |  | ||||||
| from string import ascii_lowercase |  | ||||||
| from abc import ABCMeta, abstractmethod |  | ||||||
| 
 |  | ||||||
| markerTypes=[',', '+', '.', '^', 'v', '<', '>', 'o', '*', '1', '2', '3', '4', '8', 's', 'p', 'h', 'H', 'x', 'X', 'D', 'd', '|', '_'] |  | ||||||
| #for c in ascii_lowercase: |  | ||||||
| #	markerTypes.append('$%s$' % c) |  | ||||||
| #markerColors=('r', 'g', 'b') |  | ||||||
| markerColors=('r') |  | ||||||
| 
 |  | ||||||
| def get_marker(proc_id): |  | ||||||
| 	hash_object = hashlib.md5(proc_id.encode('utf-8')) |  | ||||||
| 	hash = int(hash_object.hexdigest(), 16) |  | ||||||
| 	return markerTypes[ hash % len(markerTypes) ] |  | ||||||
| 
 |  | ||||||
| def plotCpuPassmark(): |  | ||||||
| 	cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U10", float, int, float, float), names=True, delimiter='\t') |  | ||||||
| 	plt.subplot(1,1,0) |  | ||||||
| 	plt.subplots_adjust(bottom = 0.1) |  | ||||||
| 	markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) |  | ||||||
| 	labels = cpuTable['id'] |  | ||||||
| 	x = cpuTable['clock'] * cpuTable['num_cores'] |  | ||||||
| 	y = cpuTable['cpumark'] |  | ||||||
| 	markerSize = 50 |  | ||||||
| 	color = 'b' |  | ||||||
| 	for label, x1, y1 in zip(labels, x, y): |  | ||||||
| 		if  y1 <= 0.0: |  | ||||||
| 			continue # no passmark available fo this data |  | ||||||
| 		generation=label[-1] |  | ||||||
| 		if generation == '2': |  | ||||||
| 			color = 'b' |  | ||||||
| 		else: |  | ||||||
| 			color = 'r'			 |  | ||||||
| 		marker = markersCycler.next() |  | ||||||
| 		plt.scatter( x1, y1, color = color, s = markerSize, marker = marker[0], label = label)	 |  | ||||||
| 	plt.xlabel(u'theoretical cpu speed [core.GHz]') |  | ||||||
| 	plt.ylabel(u'passmark [?]') |  | ||||||
| 	plt.title(u'comparison between cpu theoretical and effective speed') |  | ||||||
| 	plt.xlim( xmin = 0.0 ) |  | ||||||
| 	plt.ylim( ymin = 0.0 ) |  | ||||||
| 	plt.legend(bbox_to_anchor=(0.2, 1.0)) |  | ||||||
| 	#plt.legend() |  | ||||||
| 	plt.draw() |  | ||||||
| 	plt.show() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Cpu(): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, proc_id): |  | ||||||
| 		cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U15", float, int, float, float, float), names=True, delimiter='\t') |  | ||||||
| 		for id, clock, num_cores, tdp, cpumark in zip(cpuTable['id'], cpuTable['clock'], cpuTable['num_cores'], cpuTable['tdp'], cpuTable['cpumark_1_cpu']): |  | ||||||
| 			if id == proc_id: |  | ||||||
| 				# print('found '+procId) |  | ||||||
| 				break |  | ||||||
| 		assert id == proc_id, 'Failed to find %s in cputable' % proc_id |  | ||||||
| 		self.proc_id = proc_id |  | ||||||
| 		self.clock = clock |  | ||||||
| 		self.num_cores = num_cores |  | ||||||
| 		self.tdp = tdp |  | ||||||
| 		self.cpumark = cpumark |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def architecture(self): |  | ||||||
| 		proc_id = self.proc_id |  | ||||||
| 		if re.match('core-i[357]-8[0-9][0-9][0-9][ktbuh]', proc_id): |  | ||||||
| 			return 'coffeelake' |  | ||||||
| 		elif re.match('Silver-[0-9]2[0-9][0-9]', proc_id): |  | ||||||
| 			return 'cascadelake' |  | ||||||
| 		elif re.match('Gold-[0-9]2[0-9][0-9]', proc_id): |  | ||||||
| 			return 'cascadelake' |  | ||||||
| 		elif re.match('Platinum-[0-9]2[0-9][0-9]', proc_id): |  | ||||||
| 			return 'cascadelake' |  | ||||||
| 		elif re.match('Gold-[0-9]1[0-9][0-9]', proc_id): |  | ||||||
| 			return 'skylake' |  | ||||||
| 		elif re.match('Platinum-[0-9]1[0-9][0-9]', proc_id): |  | ||||||
| 			return 'skylake' |  | ||||||
| 		elif re.match('E5-26[0-9][0-9][LWA]*v4', proc_id): |  | ||||||
| 			return 'broadwell' |  | ||||||
| 		elif re.match('E5-26[0-9][0-9][LWA]*v3', proc_id): |  | ||||||
| 			return 'haswell' |  | ||||||
| 		elif re.match('E5-26[0-9][0-9][LWA]*v2', proc_id): |  | ||||||
| 			return 'ivy bridge' |  | ||||||
| 		elif re.match('E5-26[0-9][0-9][LWA]*', proc_id): |  | ||||||
| 			return 'sandy bridge' |  | ||||||
| 		elif re.match('X56[0-9][0-9]', proc_id): |  | ||||||
| 			return 'gulftown' |  | ||||||
| 		elif re.match('X55[0-9][0-9]', proc_id): |  | ||||||
| 			return 'gainestown' |  | ||||||
| 		elif re.match('E54[0-9][0-9]', proc_id): |  | ||||||
| 			return 'harpertown' |  | ||||||
| 		elif re.match('51[0-9][0-9]', proc_id): |  | ||||||
| 			return 'woodcrest' |  | ||||||
| 		else: |  | ||||||
| 			assert False |  | ||||||
| 	 |  | ||||||
| 	@property |  | ||||||
| 	def num_dp_flop_per_cycle(self): |  | ||||||
| 		proc_arch = self.architecture |  | ||||||
| 		simd_id = get_simd_id(proc_arch) |  | ||||||
| 		num_simd_per_core = 1 |  | ||||||
| 		if proc_arch == 'skylake' or proc_arch == 'cascadelake': |  | ||||||
| 		    # from https://en.wikipedia.org/wiki/List_of_Intel_Xeon_microprocessors : Xeon Platinum, Gold 61XX, and Gold 5122 have two AVX-512 FMA units per core; Xeon Gold 51XX (except 5122), Silver, and Bronze have a single AVX-512 FMA unit per core |  | ||||||
| 			if re.match('Gold-5122', self.proc_id): |  | ||||||
| 				num_simd_per_core = 2 |  | ||||||
| 
 |  | ||||||
| 			# https://en.wikichip.org/wiki/intel/xeon_gold/5222 : 'Note that this is the only processor in the Xeon Gold 52xx series with two 512b FMA units.' |  | ||||||
| 			if re.match('Gold-5222', self.proc_id): |  | ||||||
| 				num_simd_per_core = 2 |  | ||||||
| 				 |  | ||||||
| 			if re.match('Gold-61[0-9][0-9]', self.proc_id): |  | ||||||
| 				num_simd_per_core = 2 |  | ||||||
| 			if re.match('Gold-62[0-9][0-9]', self.proc_id): |  | ||||||
| 				num_simd_per_core = 2 |  | ||||||
| 		dp_flops_per_cycle = num_simd_per_core * simd_id_to_dp_flops_per_cycle(simd_id) |  | ||||||
| 		print(self.proc_id, dp_flops_per_cycle) |  | ||||||
| 		return dp_flops_per_cycle |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def num_ram_channels(self): |  | ||||||
| 		return { |  | ||||||
| 			'skylake': 6, |  | ||||||
| 			'coffeelake': 6, |  | ||||||
| 			'cascadelake': 6 |  | ||||||
| 		}[self.architecture] |  | ||||||
| 
 |  | ||||||
| def get_proc_architecture(proc_id): |  | ||||||
| 	return Cpu(proc_id).architecture |  | ||||||
| 
 |  | ||||||
| def get_proc_arch_transistor_size(proc_id):  |  | ||||||
| 	return { |  | ||||||
| 		'woodcrest':65, |  | ||||||
| 		'harpertown':45, |  | ||||||
| 		'gainestown':45, |  | ||||||
| 		'gulftown':32, |  | ||||||
| 		'sandy bridge':32, |  | ||||||
| 		'ivy bridge':22, |  | ||||||
| 		'haswell':22, |  | ||||||
| 		'broadwell':14, |  | ||||||
| 		'skylake':14, |  | ||||||
| 		'coffeelake':14, |  | ||||||
| 		'cascadelake':14 |  | ||||||
| 		}[get_proc_architecture(proc_id)] |  | ||||||
| 
 |  | ||||||
| def simd_id_to_dp_flops_per_cycle(simd_id): |  | ||||||
| 	""" |  | ||||||
| 	:param str simd_id: eg 'avx2' |  | ||||||
| 	 |  | ||||||
| 	""" |  | ||||||
| 	# from https://stackoverflow.com/questions/15655835/flops-per-cycle-for-sandy-bridge-and-haswell-sse2-avx-avx2 |  | ||||||
| 	# Intel Core 2 and Nehalem: |  | ||||||
| 	#  |  | ||||||
| 	#     4 DP FLOPs/cycle: 2-wide SSE2 addition + 2-wide SSE2 multiplication |  | ||||||
| 	#     8 SP FLOPs/cycle: 4-wide SSE addition + 4-wide SSE multiplication |  | ||||||
| 	#  |  | ||||||
| 	# Intel Sandy Bridge/Ivy Bridge: |  | ||||||
| 	#  |  | ||||||
| 	#     8 DP FLOPs/cycle: 4-wide AVX addition + 4-wide AVX multiplication |  | ||||||
| 	#     16 SP FLOPs/cycle: 8-wide AVX addition + 8-wide AVX multiplication |  | ||||||
| 	#  |  | ||||||
| 	# Intel Haswell/Broadwell/Skylake/Kaby Lake: |  | ||||||
| 	#  |  | ||||||
| 	#     16 DP FLOPs/cycle: two 4-wide FMA (fused multiply-add) instructions |  | ||||||
| 	#     32 SP FLOPs/cycle: two 8-wide FMA (fused multiply-add) instructions |  | ||||||
| 
 |  | ||||||
| 	return {	 |  | ||||||
| 		'sse4.1':4, |  | ||||||
| 		'sse4.2':4, |  | ||||||
| 		'avx':8, |  | ||||||
| 		'avx2':16, |  | ||||||
| 		'avx-512':16, |  | ||||||
| 		}[simd_id] |  | ||||||
| 	 |  | ||||||
| def get_simd_id(proc_arch): |  | ||||||
| 	""" |  | ||||||
| 		:param str proc_arch: eg 'broadwell' |  | ||||||
| 		:return str: eg 'sse4' |  | ||||||
| 	""" |  | ||||||
| 	return { |  | ||||||
| 		'woodcrest':'sse4.1', |  | ||||||
| 		'harpertown':'sse4.1', |  | ||||||
| 		'gainestown':'sse4.2', |  | ||||||
| 		'gulftown':'sse4.2', |  | ||||||
| 		'sandy bridge':'avx', |  | ||||||
| 		'ivy bridge':'avx', |  | ||||||
| 		'haswell':'avx2', |  | ||||||
| 		'broadwell':'avx2', |  | ||||||
| 		'skylake':'avx-512', |  | ||||||
| 		'cascadelake':'avx-512', |  | ||||||
| 		'coffeelake':'avx2' |  | ||||||
| 		}[proc_arch] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class HostType(): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id, num_cpu_per_server, num_servers=1): |  | ||||||
| 		self.host_type_id = host_type_id |  | ||||||
| 		self.num_cpu_per_server = num_cpu_per_server |  | ||||||
| 		self.num_servers = num_servers |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		pass |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_dimm_price(self, dimm_capacity): |  | ||||||
| 		pass |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		pass |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_disk_upgrade_price(self, disk_capacity): |  | ||||||
| 		pass |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeC6220(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=4) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		return 4890.0 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 880.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 320.0 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeR620(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		return 860.0 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 240.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return -20.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeR630(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# for r630 on 14/10/2016 |  | ||||||
| 		#   (x: price without procs, p2603: price of e5-2603v4, p2609: price of e5-2609v4) |  | ||||||
| 		#   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 |  | ||||||
| 		#   x + p2603 = 948.0 |  | ||||||
| 		#   x + 2 * p2603 = 948.0 + 216 |  | ||||||
| 		#   => p2603 approx= 215.5 |  | ||||||
| 		#   => x = 948. - 215. = 733.0 |  | ||||||
| 		# verification : |  | ||||||
| 		#   x + p2609 = 1057.0 |  | ||||||
| 		#   => p2609 = 1057-733=324.0 |  | ||||||
| 		#   x + 2 * p2609 = 1381.0 |  | ||||||
| 		return 733.0 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 240.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeR730(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# for r730 on 06/10/2016 |  | ||||||
| 		#   (x: price without procs, p1 : price of e5-2603v4, p2: price of e5-2609v4) |  | ||||||
| 		#   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 |  | ||||||
| 		#   x + p1 = 1014.0 |  | ||||||
| 		#   x + 2 * p1 = 1014.0 + 216 |  | ||||||
| 		#   => p1 approx= 215.5 |  | ||||||
| 		#   => x = 1014. - 215. = 799.0 |  | ||||||
| 		#   x + p2 = 1123.0 |  | ||||||
| 		#   => p2 = 324.0 |  | ||||||
| 		#   x + 2 * p2 = 1447.0 |  | ||||||
| 		return 799.0 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 240.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeC4130(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# for c4130 on 14/10/2016 |  | ||||||
| 		# x + 2 x E5-2640v4 + 128G + 2 * K80 + X520 + p5years = 12281€ |  | ||||||
| 		# x + 2 x E5-2640v4 + 128G + 4 * K80 + X520 + p5years = 19317€ |  | ||||||
| 		# price of a K80 |  | ||||||
| 		# >>> (19317.-12281)/2 |  | ||||||
| 		# 3518.0 |  | ||||||
| 		# assuming the options cost the same as for R630 (X520=210€, p5years=240€, 128G=1778€, E5-2640v4=951€), the cost of the base system is : |  | ||||||
| 		# >>> 12281-951-951-1778-210-240-3518-3518 |  | ||||||
| 		# 1115 |  | ||||||
| 		# but if we integrate the X520 card so that we have a 10Gb ethernet in the base, the cost of the base system becomes : |  | ||||||
| 		# >>> 1115+210 |  | ||||||
| 		# 1325 |  | ||||||
| 		return 1325.0 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 240.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeC6320(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=4) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# for 4xc6320 on 14/10/2016 |  | ||||||
| 		#   (x: price without procs, p2603: price of e5-2603v4, p2609: price of e5-2609v4) |  | ||||||
| 		# x + 4 x (2 x p2620 +  p32G) =  5135 € HT |  | ||||||
| 		# x + 4 x (2 x p2640 + p128G + pX520 + p5years) = 15590 € HT |  | ||||||
| 		# x + 4 x (2 x p2650 + p128G + pX520 + p5years) = 17340 € HT |  | ||||||
| 		# x + 4 x (2 x p2660 + p128G + pX520 + p5years) = 19490 € HT |  | ||||||
| 		# by examining this and the price of processors on R630 |  | ||||||
| 		# - E5-2620v4 :   458€ |  | ||||||
| 		# - E5-2640v4 :   951€ |  | ||||||
| 		# - E5-2650v4 :  1209€ |  | ||||||
| 		# - E5-2660v4 :  1525€ |  | ||||||
| 		# - E5-2680v4 :  1867€ |  | ||||||
| 		# - E5-2690v4 :  2261€ |  | ||||||
| 		# I could work out that : |  | ||||||
| 		# - the price of procs on c6320 is the price of procs on r630 * 85% |  | ||||||
| 		# - the price of the base c6320 with 32 Go and no proc at all is 2020.6 |  | ||||||
| 		# - the price of the 32G to 128G upgrade is 6222.6 euros (cheaper price of 16G->128G upgrade on r630 : (1778*4 = 7112)) |  | ||||||
| 		# details : |  | ||||||
| 		# 	>>> (19490.-17340)/8 |  | ||||||
| 		# 	268.75 |  | ||||||
| 		# 	>>> (17340.-15590)/8 |  | ||||||
| 		# 	218.75 |  | ||||||
| 		# 	>>> 218.75/258. |  | ||||||
| 		# 	0.8478682170542635 |  | ||||||
| 		# 	>>> 268.75/316 |  | ||||||
| 		# 	0.8504746835443038 |  | ||||||
| 		# 	>>> 15590.0+((1209.0-951.0)*0.85)*8 |  | ||||||
| 		# 	17344.4 |  | ||||||
| 		# 	>>> 15590.0+((1525.0-951.0)*0.85)*8 |  | ||||||
| 		# 	19493.2 |  | ||||||
| 		#   price of 128G ram upgrade assuming that 5years guarantee costs 880€ (same as c6220), |  | ||||||
| 		# 	>>> 15590.0+((458.0-951.0)*0.85)*8-210.0*4-880.0 - 5135.0 |  | ||||||
| 		#	6222.6 |  | ||||||
| 		# 	>>> 5135.0 - (458.0*0.85)*8 |  | ||||||
| 		# 	2020.6 |  | ||||||
| 		return 2020.6 |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 880.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeR640(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# on 29/09/2017 |  | ||||||
| 		#   (x: price without procs, p3106: price of Bronze-3106, p6126: price of Gold6126) |  | ||||||
| 		#   we want to know x, given dell's web site, where we can get the price for multiple proc but not 0 |  | ||||||
| 		#   x + p3106 = 1067.0 |  | ||||||
| 		#   x + 2 * p3106 = 1067.0 + 320.0 |  | ||||||
| 		#   => p3106 = 320 |  | ||||||
| 		#   => x = 1067.0 - 320.0 = 747.0 |  | ||||||
| 		# 	check if x computation is consistent with p6126 |  | ||||||
| 		#   x + p6126 = 2767 |  | ||||||
| 		#   x + 2 * p6126 = 4787.0 |  | ||||||
| 		#   => p6126 = 2020.0 |  | ||||||
| 		#   => x = 747.0 --> yes ! |  | ||||||
| 		return 747.0 |  | ||||||
| 
 |  | ||||||
| 	def get_dimm_price(self, dimm_capacity): |  | ||||||
| 		return { |  | ||||||
| 			8: 80.0, |  | ||||||
| 			16: 160.0, |  | ||||||
| 			32: 320.0, |  | ||||||
| 			64: 640.0 |  | ||||||
| 		}[dimm_capacity] |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		if guarantee_duration > 7: |  | ||||||
| 			assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id |  | ||||||
| 		elif guarantee_duration >= 5: |  | ||||||
| 			return 270.0  # from dell matinfo4 online quotation |  | ||||||
| 		else: |  | ||||||
| 			# 5-year guarantee included in base price |  | ||||||
| 			return 0.0 * self.num_servers |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		# Retrait des disques de base (2x600Go 10K SAS 2.5'') : -260.0 € |  | ||||||
| 		# Ajout d'un disque dur 1,2 To SAS 10k Tpm 2,5" - hotplug : 165.0 € |  | ||||||
| 		base_disks_removal_price = -260.0 |  | ||||||
| 		disk_1200g_price = 165.0 |  | ||||||
| 		return (base_disks_removal_price + disk_1200g_price * 2) * self.num_servers |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeR940(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=4, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# price of r940 (with 2x xeon gold 5215 and 32 Go DDR4 @ 2933GHz) on 09/06/2020 : 3784€ |  | ||||||
| 		#   (x: price without procs, p5215: price of gold-5215, p6248: price of Gold6248) |  | ||||||
| 		#   p6240 = 2684 |  | ||||||
| 		#   p6248 = 3442 |  | ||||||
| 		#   p8280l = 12075 |  | ||||||
| 		#   x + 2 * p5215 = 3784 |  | ||||||
| 		#   x + 4 * p6240 = 11886   =>    x = 1150 |  | ||||||
| 		#   x + 4 * p6248 = 14918   =>    x = 1150 |  | ||||||
| 		#   x + 4 * p8280l = 49450  =>    x = 1150 |  | ||||||
| 		#   => p5215 = 1317   (agrees with proc price on r640) |  | ||||||
| 		return 1150.0 |  | ||||||
| 
 |  | ||||||
| 	def get_dimm_price(self, dimm_capacity): |  | ||||||
| 		return { |  | ||||||
| 			8: 80.0, |  | ||||||
| 			16: 160.0, |  | ||||||
| 			32: 320.0, |  | ||||||
| 			64: 640.0 |  | ||||||
| 		}[dimm_capacity] |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		if guarantee_duration > 7: |  | ||||||
| 			assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id |  | ||||||
| 		elif guarantee_duration >= 5: |  | ||||||
| 			return 630.0  # from dell matinfo4 online quotation |  | ||||||
| 		else: |  | ||||||
| 			# 5-year guarantee included in base price |  | ||||||
| 			return 0.0 |  | ||||||
| 
 |  | ||||||
| 	@abstractmethod |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		# Retrait des disques de base (2x600Go 10K SAS 2.5'') : -260.0 € |  | ||||||
| 		# Ajout d'un disque dur 1,2 To SAS 10k Tpm 2,5" - hotplug : 165.0 € |  | ||||||
| 		base_disks_removal_price = -260.0 |  | ||||||
| 		disk_1200g_price = 165.0 |  | ||||||
| 		return (base_disks_removal_price + disk_1200g_price * 2) * self.num_servers |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DellPrecision3630(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=1, num_servers=1) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		return 449.0 |  | ||||||
| 
 |  | ||||||
| 	def get_dimm_price(self, dimm_capacity): |  | ||||||
| 		return { |  | ||||||
| 			8: 80.0, |  | ||||||
| 			16: 160.0, |  | ||||||
| 			32: 320.0 |  | ||||||
| 		}[dimm_capacity] |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		assert guarantee_duration <= 5, 'only 5 year guarantee is handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		return 0.0 |  | ||||||
| 
 |  | ||||||
| class DellPowerEdgeC6420(HostType): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		super().__init__(host_type_id, num_cpu_per_server=2, num_servers=4) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		# for 4xc6420 on 19/06/2020 (from excel quotation) |  | ||||||
| 		#  |  | ||||||
| 		#  |  | ||||||
| 		#   (x: price without procs, p2630: price of xeon gold 6230) |  | ||||||
| 		# x + 4 x (2 x p4210r + p48g) =  5368 € HT |  | ||||||
| 		# x + 4 x (2 x p6230r + p192g) = 27213 € HT |  | ||||||
| 		#  |  | ||||||
| 		# p48g = 3 * 160.0  # the price of a 16G ram is 160.0 € |  | ||||||
| 		# p4210r = p4210r=978./2 # from r640 prices |  | ||||||
| 		#  |  | ||||||
| 
 |  | ||||||
| 		# >>> p4210r=978./2 |  | ||||||
| 		# >>> p6230r_upgrade = 13408.0 |  | ||||||
| 		# >>> p6230r_for_r640 = 2165.0 |  | ||||||
| 		# >>> num_servers_per_c6000 = 4 |  | ||||||
| 		# >>> num_cpu_per_server = 2 |  | ||||||
| 		# >>> p6230r_for_c6420 = (p6230r_upgrade + p4210r * (num_servers_per_c6000 * num_cpu_per_server))/(num_servers_per_c6000 * num_cpu_per_server) |  | ||||||
| 		# >>> p6230r_for_c6420 |  | ||||||
| 		# 2165.0 |  | ||||||
| 
 |  | ||||||
| 		# => p4210r seems to be the same on r640 and c6420 |  | ||||||
| 		#  |  | ||||||
| 		# pc6000 = 5368 - (p4210r * num_cpu_per_server + p48g) * num_servers_per_c6000 |  | ||||||
| 		# >>> p16g = 160.0 |  | ||||||
| 		# >>> p48g = p16g * 3 |  | ||||||
| 		# >>> pc6000 = 5368 - (p4210r * num_cpu_per_server + p48g) * num_servers_per_c6000 |  | ||||||
| 		# >>> pc6000 |  | ||||||
| 		# -464.0 |  | ||||||
| 		# >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) |  | ||||||
| 		# Traceback (most recent call last): |  | ||||||
| 		#   File "<stdin>", line 1, in <module> |  | ||||||
| 		# NameError: name 'p192g' is not defined |  | ||||||
| 		# >>> p192g = (192/16)*p16g |  | ||||||
| 		# >>> p192g |  | ||||||
| 		# 1920.0 |  | ||||||
| 		# >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) |  | ||||||
| 		# 24536.0 |  | ||||||
| 		# >>> pc6000 + num_servers_per_c6000 * (p6230r_for_c6420 * num_cpu_per_server + p192g) + 1159 + 68 + 350 + 1100 |  | ||||||
| 		# 27213.0 |  | ||||||
| 		num_servers_per_c6000 = 4 |  | ||||||
| 		num_cpu_per_server = 2 |  | ||||||
| 		ram_price_per_gigabyte = 160.0 / 16 # 16G ram price : 160.0 € |  | ||||||
| 		xeon_silver_4210r_price = 978.0 / 2 # from r640 prices |  | ||||||
| 		basic_config_price = 5368.0 |  | ||||||
| 		poweredge_c6000_price = basic_config_price - (xeon_silver_4210r_price * num_cpu_per_server + ram_price_per_gigabyte * 48) * num_servers_per_c6000 |  | ||||||
| 		return poweredge_c6000_price |  | ||||||
| 
 |  | ||||||
| 	def get_dimm_price(self, dimm_capacity): |  | ||||||
| 		return { |  | ||||||
| 			8: 80.0, |  | ||||||
| 			16: 160.0, |  | ||||||
| 			32: 320.0, |  | ||||||
| 			64: 640.0 |  | ||||||
| 		}[dimm_capacity] |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		if guarantee_duration > 7: |  | ||||||
| 			assert False, 'guarantee of more than 7 years is not available on %s' % self.host_type_id |  | ||||||
| 		elif guarantee_duration >= 5: |  | ||||||
| 			return 1100.0  # from c6420-20200716-price |  | ||||||
| 		else: |  | ||||||
| 			# 5-year guarantee included in base price |  | ||||||
| 			return 0.0 |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		assert 1.9e12 < asked_disk_capacity < 2.1e12, 'only 2To upgrades are handled for %s' % self.host_type_id |  | ||||||
| 		# from c6420-20200716-price |  | ||||||
| 		# | Ajout d'un disque dur 1 To SATA 7200 Tpm 3,5'' pour les 4 serveurs                                                                                                                                                                                                        |                                         | 4-3-1-14g096         | C6420   | 361 €          |     | € -         | |  | ||||||
| 		return 361.0 |  | ||||||
| 
 |  | ||||||
| def create_host_type(host_type_id): |  | ||||||
| 	if host_type_id == 'c6420': |  | ||||||
| 		return DellPowerEdgeC6420(host_type_id) |  | ||||||
| 	if host_type_id == 'c6320': |  | ||||||
| 		return DellPowerEdgeC6320(host_type_id) |  | ||||||
| 	if host_type_id == 'c4130': |  | ||||||
| 		return DellPowerEdgeC4130(host_type_id) |  | ||||||
| 	if host_type_id == 'r620': |  | ||||||
| 		return DellPowerEdgeR620(host_type_id) |  | ||||||
| 	if host_type_id == 'r630': |  | ||||||
| 		return DellPowerEdgeR630(host_type_id) |  | ||||||
| 	if host_type_id == 'r640': |  | ||||||
| 		return DellPowerEdgeR640(host_type_id) |  | ||||||
| 	if host_type_id == 'r940': |  | ||||||
| 		return DellPowerEdgeR940(host_type_id) |  | ||||||
| 	if host_type_id == 'precision3630': |  | ||||||
| 		return DellPrecision3630(host_type_id) |  | ||||||
| 	assert False |  | ||||||
| 
 |  | ||||||
| class Config(): |  | ||||||
| 
 |  | ||||||
| 	def __init__(self, host_type_id): |  | ||||||
| 		self.host_type = create_host_type(host_type_id) |  | ||||||
| 
 |  | ||||||
| 	def get_empty_price(self): |  | ||||||
| 		return self.host_type.get_empty_price() |  | ||||||
| 
 |  | ||||||
| 	def get_ram_update_price(self, cpu, ram_per_core=None, ram_per_server=None, ram_per_cpu=None): |  | ||||||
| 
 |  | ||||||
| 		# ramUpgradePrice128Gb = { |  | ||||||
| 		# 	'c6220':3520.0, |  | ||||||
| 		# 	'r620':2010.0,  |  | ||||||
| 		# 	'r630':1778.0, |  | ||||||
| 		# 	'r640':1780.0, |  | ||||||
| 		# 	'r730':1778.0, |  | ||||||
| 		# 	'r940':960.0,   # 32 Gb 2933 MHz RDIMM : 320 € |  | ||||||
| 		# 	'c6320':6222.6, |  | ||||||
| 		# 	'c4310':1778.0, |  | ||||||
| 		# 	'precision3630': 1536.0 } |  | ||||||
| 		if ram_per_cpu: |  | ||||||
| 			assert not ram_per_core |  | ||||||
| 			assert not ram_per_server |  | ||||||
| 		if ram_per_core: |  | ||||||
| 			assert not ram_per_server |  | ||||||
| 			assert not ram_per_cpu |  | ||||||
| 			ram_per_cpu = cpu.num_cores * ram_per_core |  | ||||||
| 		if ram_per_server: |  | ||||||
| 			assert not ram_per_core |  | ||||||
| 			assert not ram_per_cpu |  | ||||||
| 			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.host_type.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 |  | ||||||
| 		ram_price = self.num_servers * self.num_cpu_per_server * cpu.num_ram_channels * self.host_type.get_dimm_price(dimm_capacity) |  | ||||||
| 		print("ram_price : %f € for %d dimms of %d Gb" % (ram_price, self.num_servers * self.num_cpu_per_server * cpu.num_ram_channels, dimm_capacity)) |  | ||||||
| 		return ram_price |  | ||||||
| 
 |  | ||||||
| 	def get_guarantee_price(self, guarantee_duration): |  | ||||||
| 		return self.host_type.get_guarantee_price(guarantee_duration) |  | ||||||
| 
 |  | ||||||
| 	def get_disk_upgrade_price(self, asked_disk_capacity): |  | ||||||
| 		return self.host_type.get_disk_upgrade_price(asked_disk_capacity) |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def num_servers(self): |  | ||||||
| 		return self.host_type.num_servers |  | ||||||
| 
 |  | ||||||
| 	@property |  | ||||||
| 	def num_cpu_per_server(self): |  | ||||||
| 		return self.host_type.num_cpu_per_server |  | ||||||
| 	 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def plotSystemEfficiency(): |  | ||||||
| 
 |  | ||||||
| 	cpuTable = numpy.genfromtxt('cpu_table.dat', dtype=("|U15", float, int, float, float, float), names=True, delimiter='\t') |  | ||||||
| 	#cpuTable = numpy.genfromtxt('dell_ivybridge_table.dat', dtype=(('id', "|S10"), ('clock', float), ('num_cores', int), ('price', float, float)), names=None, delimiter='\t') |  | ||||||
| 	print(type(cpuTable)) |  | ||||||
| 	print(cpuTable.dtype) |  | ||||||
| 	print(cpuTable) |  | ||||||
| 	print(cpuTable['id']) |  | ||||||
| 
 |  | ||||||
| 	dellPriceTable = numpy.genfromtxt('dell_procoptions_table.dat', dtype=("|U15", "|U15", float), names=True, delimiter='\t') |  | ||||||
| 	#cpuTable = numpy.genfromtxt('dell_ivybridge_table.dat', dtype=(('id', "|S10"), ('clock', float), ('num_cores', int), ('price', float, float)), names=None, delimiter='\t') |  | ||||||
| 
 |  | ||||||
| 	#for (x, y) in clusters: |  | ||||||
| 	serverBasePowerConsumption = 100.0 # rough estimation in watts |  | ||||||
| 	def GHzToMHz( frequency ): |  | ||||||
| 		return frequency * 1000.0 |  | ||||||
| 		 |  | ||||||
| 	kWHPrice = 0.07 * 1.5 |  | ||||||
| 	containerLifetime = 7.0 # in years |  | ||||||
| 	powerUsageEfficiency = 0.5 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	 |  | ||||||
| 	def getColorCodeFromItemLabel(label): |  | ||||||
| 		generation=label[-1] |  | ||||||
| 		(model, proc_id) = re.split('_', label) |  | ||||||
| 		saturation = { |  | ||||||
| 			'sandy bridge':0.0, |  | ||||||
| 			'ivy bridge':0.2, |  | ||||||
| 			'haswell':0.2, |  | ||||||
| 			'broadwell':0.2, |  | ||||||
| 			'skylake':0.4, |  | ||||||
| 			'coffeelake':0.6, |  | ||||||
| 			'cascadelake':1.0 |  | ||||||
| 			}[get_proc_architecture(proc_id)] |  | ||||||
| 			# 		if model == 'r620': |  | ||||||
| 			# 			color = 'r' |  | ||||||
| 			# 		elif model == 'r630': |  | ||||||
| 			# 			color = 'g' |  | ||||||
| 			# 		elif model == 'r730': |  | ||||||
| 			# 			color = 'm' |  | ||||||
| 			# 		elif model == 'c6220': |  | ||||||
| 			# 			if generation == '2': |  | ||||||
| 			# 				color = 'b' |  | ||||||
| 			# 			else: |  | ||||||
| 			# 				color = 'y' |  | ||||||
| 		hue = { |  | ||||||
| 			'r620': 0.6, |  | ||||||
| 			'r630': 0.6, |  | ||||||
| 			'r640': 0.6, |  | ||||||
| 			'c4310': 0.6, |  | ||||||
| 			'r730': 0.4, |  | ||||||
| 			'r940': 0.8, |  | ||||||
| 			'c6220': 1.0, |  | ||||||
| 			'c6320': 1.0, |  | ||||||
| 			'c6420': 1.0, |  | ||||||
| 			'precision3630': 0.2 |  | ||||||
| 			}[model] |  | ||||||
| 		value = 0.9 |  | ||||||
| 		return matplotlib.colors.hsv_to_rgb((hue, saturation, value)) |  | ||||||
| 
 |  | ||||||
| 	def get_marker_from_label(label): |  | ||||||
| 		(model, proc_id) = re.split('_', label) |  | ||||||
| 		return get_marker(proc_id) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	itemPrice = numpy.array([]) |  | ||||||
| 	itemPowerConsumption = numpy.array([]) |  | ||||||
| 	itemSpeed = numpy.array([]) |  | ||||||
| 	itemLabel = numpy.array([]) |  | ||||||
| 	itemGeneration = numpy.array([]) |  | ||||||
| 	for hostTypeId, procId, procOptionPrice in zip(dellPriceTable['host_type_id'], dellPriceTable['proc_id'], dellPriceTable['proc_option_price']): |  | ||||||
| 		print(hostTypeId, procId) |  | ||||||
| 		#if hostTypeId == 'r630': |  | ||||||
| 		#	continue |  | ||||||
| 		cpu = Cpu(procId) |  | ||||||
| 		if not cpu.architecture in ['coffeelake', 'skylake','cascadelake']: |  | ||||||
| 			continue |  | ||||||
| 		 |  | ||||||
| 		config = Config(hostTypeId) |  | ||||||
| 
 |  | ||||||
| 		itemGeneration = procId[-1] |  | ||||||
| 		 |  | ||||||
| 		itemLabel = numpy.append( itemLabel, hostTypeId + '_' + procId ) |  | ||||||
| 		print('procOptionPrice', procOptionPrice) |  | ||||||
| 		config_price = procOptionPrice |  | ||||||
| 		config_price += config.get_empty_price() |  | ||||||
| 		print('config.get_empty_price()', config.get_empty_price()) |  | ||||||
| 		ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_core=6.0e9) |  | ||||||
| 		# ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_server=192.0e9) |  | ||||||
| 		# ram_update_price = config.get_ram_update_price(cpu=cpu, ram_per_cpu=96.0e9) |  | ||||||
| 		print('ram_update_price', ram_update_price) |  | ||||||
| 		config_price += ram_update_price |  | ||||||
| 		config_price += config.get_guarantee_price(5) |  | ||||||
| 		print('config.config.get_guarantee_price(5)', config.get_guarantee_price(5)) |  | ||||||
| 		config_price += config.get_disk_upgrade_price(2.0e12) |  | ||||||
| 		print('config.get_disk_upgrade_price(2.0e12)', config.get_disk_upgrade_price(2.0e12)) |  | ||||||
| 		itemPrice = numpy.append( itemPrice, config_price ) |  | ||||||
| 		itemPowerConsumption = numpy.append( itemPowerConsumption, (cpu.tdp * config.num_cpu_per_server+serverBasePowerConsumption) * config.num_servers ) |  | ||||||
| 		# print(hostTypeId, procId, itemPowerConsumption[-1]) |  | ||||||
| 		itemSpeed = numpy.append( itemSpeed, cpu.num_dp_flop_per_cycle * cpu.clock * 1.e9 * cpu.num_cores * config.num_cpu_per_server * config.num_servers) |  | ||||||
| 
 |  | ||||||
| 	#pylab.plot(x, y, '+') |  | ||||||
| 	#pylab.xlabel('speed/price ratio [core.MHz/euros]') |  | ||||||
| 	#pylab.ylabel('speed/power consumption ratio [core.MHz/W]') |  | ||||||
| 	#pylab.show() # or savefig(<filename>) |  | ||||||
| 
 |  | ||||||
| 	 |  | ||||||
| 	#print("items = ") |  | ||||||
| 	#print(itemLabel) |  | ||||||
| 
 |  | ||||||
| 	markerSize = 50 |  | ||||||
| 
 |  | ||||||
| 	if False: |  | ||||||
| 		plt.subplot(1,2,1) |  | ||||||
| 		plt.subplots_adjust(bottom = 0.1) |  | ||||||
| 		markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) |  | ||||||
| 		x = itemSpeed / itemPrice |  | ||||||
| 		y = itemSpeed / itemPowerConsumption |  | ||||||
| 		for label, x1, y1, power, speed, price, in zip(itemLabel, x, y, itemPowerConsumption, itemSpeed, itemPrice): |  | ||||||
| 			marker = markersCycler.next() |  | ||||||
| 			color = getColorCodeFromItemLabel(label) |  | ||||||
| 			plt.scatter( x1, y1, color = color, s = markerSize, marker = marker[0], label = label) |  | ||||||
| 			#print(x1, y1, color, markerSize, marker[0], label) |  | ||||||
| 		if False: |  | ||||||
| 			plt.scatter( x, y, marker = 'o') |  | ||||||
| 			for label, x1, y1, power, speed, price, in zip(itemLabel, x, y, itemPowerConsumption, itemSpeed, itemPrice): |  | ||||||
| 				#print(label) |  | ||||||
| 				plt.annotate( u'%s (%.1f core.GHz, %.0f W, %.0f €)' % (label,speed/1000.0, power, price),  |  | ||||||
| 					xy = (x1, y1), xytext = (-50, 50), |  | ||||||
| 					textcoords = 'offset points', ha = 'right', va = 'bottom', |  | ||||||
| 					bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), |  | ||||||
| 					arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) |  | ||||||
| 		plt.xlabel(u'speed/price ratio [core.MHz/€]') |  | ||||||
| 		plt.ylabel(u'speed/power consumption ratio [core.MHz/W]') |  | ||||||
| 		plt.xlim( xmin = 0.0 ) |  | ||||||
| 		plt.ylim( ymin = 0.0 ) |  | ||||||
| 
 |  | ||||||
| 	plt.subplot(1,2,1) |  | ||||||
| 	#fig = plt.figure() |  | ||||||
| 	#ax = fig.gca() |  | ||||||
| 	#ax.set_xticks(numpy.arange(0,1,0.1)) |  | ||||||
| 	#ax.set_yticks(numpy.arange(0,1.,0.1)) |  | ||||||
| 
 |  | ||||||
| 	powerUsedInLifetime = (itemPowerConsumption * containerLifetime * 365 * 24) / powerUsageEfficiency |  | ||||||
| 	itemTotalCost = itemPrice + (powerUsedInLifetime / 1000.0 * kWHPrice ) |  | ||||||
| 	markersCycler = itertools.cycle(itertools.product(markerTypes, markerColors)) |  | ||||||
| 	item_flops = itemSpeed |  | ||||||
| 	# print item_flops |  | ||||||
| 	item_total_num_ops = item_flops * containerLifetime * 365 * 24 * 3600 |  | ||||||
| 	# print(itemPrice) |  | ||||||
| 	x = itemPrice |  | ||||||
| 	y = item_total_num_ops / itemTotalCost |  | ||||||
| 	for i in range(len(itemLabel)): |  | ||||||
| 		print(itemLabel[i], itemPrice[i], y[i]) |  | ||||||
| 		print('itemTotalCost', itemTotalCost[i]) |  | ||||||
| 		print('flops', item_flops[i]) |  | ||||||
| 	# print y |  | ||||||
| 	for label, x1, y1, power, speed, price, in zip(itemLabel, x, y, itemPowerConsumption, itemSpeed, itemPrice): |  | ||||||
| 		if y1 > 0.0001: |  | ||||||
| 			color = getColorCodeFromItemLabel(label) |  | ||||||
| 			# marker = markersCycler.next() |  | ||||||
| 			marker = get_marker_from_label( label ) |  | ||||||
| 			#print(x1, y1) |  | ||||||
| 			plt.scatter( x1, y1, facecolors = color, s = markerSize, marker = marker[0], label = label) |  | ||||||
| 			if y1 > 5.0e16: |  | ||||||
| 				plt.annotate( u'%s' % label,  |  | ||||||
| 					xy = (x1, y1), xytext = (x1*4.0, (y1-5.2e16)*7.1), |  | ||||||
| 					textcoords = 'data', ha = 'right', va = 'bottom', |  | ||||||
| 					bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), |  | ||||||
| 					arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) |  | ||||||
| 
 |  | ||||||
| 	plt.xlabel(u'purchase price [€]') |  | ||||||
| 	plt.ylabel(u'num total DP operations/total cost [€/^-1]') |  | ||||||
| 	plt.title(u'total cost including electricity') |  | ||||||
| 	plt.xlim( xmin = 0.0 ) |  | ||||||
| 	plt.ylim( ymin = 0.0 ) |  | ||||||
| 	plt.minorticks_on() |  | ||||||
| 	plt.grid(b=True, which='major', color='b', linestyle='-', linewidth=0.5) |  | ||||||
| 	plt.grid(b=True, which='minor', color='b', linestyle='-', linewidth=0.2)	 |  | ||||||
| 	plt.legend(bbox_to_anchor=(1.1, 1.1), ncol=3) |  | ||||||
| 	plt.draw() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	plt.show() |  | ||||||
| 
 |  | ||||||
| #plotCpuPassmark(): |  | ||||||
| plotSystemEfficiency() |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -1,3 +1,4 @@ | ||||||
| # ConCho : Configuration Choser | # ConCho : Configuration Choser | ||||||
| 
 | 
 | ||||||
| a tool to help choosing the best quality price ratio for a compute node | a tool to help choosing the best quality price ratio for a compute node | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | from setuptools import setup, find_packages | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | setup(name='concho', | ||||||
|  |     version='1.0', | ||||||
|  |     packages=find_packages(), | ||||||
|  |     install_requires=['lxml', 'numpy', 'matplotlib']) | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | from concho.dell import DellConfiguratorParser | ||||||
|  | from concho.procs_chooser import plot_system_efficiency | ||||||
|  | 
 | ||||||
|  | def test_function(): | ||||||
|  |     # configurator = DellMatinfoConfigurator('rcrc1406676-4834664 - Cat2 Conf4 PowerEdge R640 - Dell.html') | ||||||
|  |     # print(configurator) | ||||||
|  |     plot_system_efficiency() | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     test_function() | ||||||
		Loading…
	
		Reference in New Issue