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