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:
Guillaume Raffy 2020-09-25 16:35:13 +02:00
parent d2fb11551e
commit 93a8b77231
10 changed files with 1977 additions and 1019 deletions

0
concho/__init__.py Normal file
View File

371
concho/config.py Normal file
View File

@ -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)

587
concho/dell.py Normal file
View File

@ -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)

242
concho/procs_chooser.py Normal file
View File

@ -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()

View File

@ -1,158 +1,157 @@
#id clock num_cores tdp cpumark_1_cpu cpumark_2_cpu
core-i7-8700k 3.7 6 95 0 0
5150 2.66 2 65 1746 3498
E5462 2.8 4 80 4038 7350
X5550 2.67 4 95 5416 9131
X5560 2.8 4 95 5426 9215
X5650 2.67 6 95 7601 11728
X5660 2.8 6 95 7954 11893
E5-2660 2.2 8 95 11535 17282
E5-2670 2.6 8 115 0 18509
E5-2680 2.7 8 130 0 18480
E5-2690 2.9 8 135 0 20699
E5-2620v2 2.1 6 95 8664 13474
E5-2630v2 2.6 6 80 10615 16256
E5-2640v2 2 8 95 10132 14792
E5-2650v2 2.6 8 95 13276 19333
E5-2660v2 2.2 10 95 13659 18670
E5-2670v2 2.5 10 115 14892 22062
E5-2680v2 2.8 10 115 16340 23263
E5-2690v2 3 10 130 17304 24189
E5-2695v2 2.4 12 130 17231 21980
E5-2697v2 2.7 12 130 17516 23910
E5-2603v3 1.6 6 85 4996 8358
E5-2609v3 1.9 6 85 5878 9885
E5-2620v3 2.4 6 85 9955 15400
E5-2623v3 3 6 105 9007 13914
E5-2630v3 2.4 8 85 12803 18699
E5-2630Lv3 1.8 8 55 11201 0
E5-2637v3 3.5 4 135 10278 16823
E5-2640v3 2.6 8 90 14081 20824
E5-2643v3 3.4 6 135 13671 20574
E5-2650v3 2.3 10 105 15106 20602
E5-2650Lv3 1.8 12 65 13132 0
E5-2660v3 2.6 10 105 16161 23388
E5-2667v3 3.2 8 135 16125 22935
E5-2670v3 2.3 12 120 16549 22330
E5-2680v3 2.5 12 120 18840 25352
E5-2683v3 2 14 120 17917 22704
E5-2687Wv3 3.1 10 160 17785 24769
E5-2690v3 2.6 12 160 19567 26665
E5-2695v3 2.3 14 120 20742 26021
E5-2697v3 2.6 14 145 21667 29009
E5-2698v3 2.3 16 135 21794 30217
E5-2699v3 2.3 18 145 22520 24820
E5-2603v4 1.7 6 85 5247 8809
E5-2609v4 1.7 8 85 0 10835
E5-2620v4 2.1 8 85 11219 17063
E5-2623v4 2.6 4 85 0 10196
E5-2630v4 2.2 10 85 14221 18281
E5-2630Lv4 1.8 10 55 0 0
E5-2637v4 3.5 4 135 9665 17398
E5-2640v4 2.4 10 90 15244 21556
E5-2643v4 3.4 6 135 14329 22063
E5-2650v4 2.2 12 105 16212 22619
E5-2650Lv4 1.7 14 65 0 0
E5-2660v4 2.0 14 105 0 0
E5-2667v4 3.2 8 135 0 0
E5-2680v4 2.4 14 120 20489 0
E5-2683v4 2.1 16 120 0 0
E5-2687Wv4 3.0 12 160 20340 27161
E5-2690v4 2.6 14 120 22843 28262
E5-2695v4 2.1 18 135 19351 20768
E5-2697v4 2.3 18 135 23070 0
E5-2697Av4 2.6 16 145 0 24075
E5-2698v4 2.2 20 135 24615 32248
E5-2699v4 2.2 22 145 21277 38461
Gold-5115 2.4 10 85 0 0
Gold-5117 2.0 14 105 0 0
Gold-5117F 2.0 14 113 0 0
Gold-5117M 2.0 0 0 0 21250
Gold-5118 2.3 12 105 0 0
Gold-5119T 1.9 14 85 0 0
Gold-5120 2.2 14 105 0 0
Gold-5120T 2.2 14 105 0 0
Gold-5122 3.6 4 105 0 0
Gold-6126 2.6 12 125 0 0
Gold-6126F 2.6 12 135 0 0
Gold-6128 3.4 6 115 0 0
Gold-6130 2.1 16 125 0 0
Gold-6130F 2.1 16 125 0 0
Gold-6130T 2.1 16 125 0 0
Gold-6132 2.6 14 140 0 0
Gold-6134 3.2 8 130 0 0
Gold-6134M 3.2 8 130 0 0
Gold-6136 3.0 12 150 0 0
Gold-6138 2.0 20 125 0 0
Gold-6138F 2.0 20 135 0 0
Gold-6138T 2.0 20 125 0 0
Gold-6140 2.3 18 140 0 0
Gold-6140M 2.3 18 140 0 0
Gold-6142 2.6 16 150 0 0
Gold-6142F 2.6 16 160 0 0
Gold-6142M 2.6 16 150 0 0
Gold-6144 3.5 8 150 0 0
Gold-6145 2.0 20 145 0 0
Gold-6146 3.2 12 165 0 0
Gold-6148 2.4 20 150 0 0
Gold-6150 2.7 18 165 0 26349
Gold-6152 2.1 22 140 0 0
Gold-6154 3.0 18 200 0 0
Gold-6161 2.2 22 165 0 0
intel-core-i7-8700k 3.7 6 95 0 0
intel-xeon-5150 2.66 2 65 1746 3498
intel-xeon-e5462 2.8 4 80 4038 7350
intel-xeon-x5550 2.67 4 95 5416 9131
intel-xeon-x5560 2.8 4 95 5426 9215
intel-xeon-x5650 2.67 6 95 7601 11728
intel-xeon-x5660 2.8 6 95 7954 11893
intel-xeon-e5-2660 2.2 8 95 11535 17282
intel-xeon-e5-2670 2.6 8 115 0 18509
intel-xeon-e5-2680 2.7 8 130 0 18480
intel-xeon-e5-2690 2.9 8 135 0 20699
intel-xeon-e5-2620v2 2.1 6 95 8664 13474
intel-xeon-e5-2630v2 2.6 6 80 10615 16256
intel-xeon-e5-2640v2 2 8 95 10132 14792
intel-xeon-e5-2650v2 2.6 8 95 13276 19333
intel-xeon-e5-2660v2 2.2 10 95 13659 18670
intel-xeon-e5-2670v2 2.5 10 115 14892 22062
intel-xeon-e5-2680v2 2.8 10 115 16340 23263
intel-xeon-e5-2690v2 3 10 130 17304 24189
intel-xeon-e5-2695v2 2.4 12 130 17231 21980
intel-xeon-e5-2697v2 2.7 12 130 17516 23910
intel-xeon-e5-2603v3 1.6 6 85 4996 8358
intel-xeon-e5-2609v3 1.9 6 85 5878 9885
intel-xeon-e5-2620v3 2.4 6 85 9955 15400
intel-xeon-e5-2623v3 3 6 105 9007 13914
intel-xeon-e5-2630v3 2.4 8 85 12803 18699
intel-xeon-e5-2630lv3 1.8 8 55 11201 0
intel-xeon-e5-2637v3 3.5 4 135 10278 16823
intel-xeon-e5-2640v3 2.6 8 90 14081 20824
intel-xeon-e5-2643v3 3.4 6 135 13671 20574
intel-xeon-e5-2650v3 2.3 10 105 15106 20602
intel-xeon-e5-2650lv3 1.8 12 65 13132 0
intel-xeon-e5-2660v3 2.6 10 105 16161 23388
intel-xeon-e5-2667v3 3.2 8 135 16125 22935
intel-xeon-e5-2670v3 2.3 12 120 16549 22330
intel-xeon-e5-2680v3 2.5 12 120 18840 25352
intel-xeon-e5-2683v3 2 14 120 17917 22704
intel-xeon-e5-2687wv3 3.1 10 160 17785 24769
intel-xeon-e5-2690v3 2.6 12 160 19567 26665
intel-xeon-e5-2695v3 2.3 14 120 20742 26021
intel-xeon-e5-2697v3 2.6 14 145 21667 29009
intel-xeon-e5-2698v3 2.3 16 135 21794 30217
intel-xeon-e5-2699v3 2.3 18 145 22520 24820
intel-xeon-e5-2603v4 1.7 6 85 5247 8809
intel-xeon-e5-2609v4 1.7 8 85 0 10835
intel-xeon-e5-2620v4 2.1 8 85 11219 17063
intel-xeon-e5-2623v4 2.6 4 85 0 10196
intel-xeon-e5-2630v4 2.2 10 85 14221 18281
intel-xeon-e5-2630lv4 1.8 10 55 0 0
intel-xeon-e5-2637v4 3.5 4 135 9665 17398
intel-xeon-e5-2640v4 2.4 10 90 15244 21556
intel-xeon-e5-2643v4 3.4 6 135 14329 22063
intel-xeon-e5-2650v4 2.2 12 105 16212 22619
intel-xeon-e5-2650lv4 1.7 14 65 0 0
intel-xeon-e5-2660v4 2.0 14 105 0 0
intel-xeon-e5-2667v4 3.2 8 135 0 0
intel-xeon-e5-2680v4 2.4 14 120 20489 0
intel-xeon-e5-2683v4 2.1 16 120 0 0
intel-xeon-e5-2687wv4 3.0 12 160 20340 27161
intel-xeon-e5-2690v4 2.6 14 120 22843 28262
intel-xeon-e5-2695v4 2.1 18 135 19351 20768
intel-xeon-e5-2697v4 2.3 18 135 23070 0
intel-xeon-e5-2697av4 2.6 16 145 0 24075
intel-xeon-e5-2698v4 2.2 20 135 24615 32248
intel-xeon-e5-2699v4 2.2 22 145 21277 38461
intel-xeon-gold-5115 2.4 10 85 0 0
intel-xeon-gold-5117 2.0 14 105 0 0
intel-xeon-gold-5117f 2.0 14 113 0 0
intel-xeon-gold-5117m 2.0 0 0 0 21250
intel-xeon-gold-5118 2.3 12 105 0 0
intel-xeon-gold-5119t 1.9 14 85 0 0
intel-xeon-gold-5120 2.2 14 105 0 0
intel-xeon-gold-5120t 2.2 14 105 0 0
intel-xeon-gold-5122 3.6 4 105 0 0
intel-xeon-gold-6126 2.6 12 125 0 0
intel-xeon-gold-6126f 2.6 12 135 0 0
intel-xeon-gold-6128 3.4 6 115 0 0
intel-xeon-gold-6130 2.1 16 125 0 0
intel-xeon-gold-6130f 2.1 16 125 0 0
intel-xeon-gold-6130t 2.1 16 125 0 0
intel-xeon-gold-6132 2.6 14 140 0 0
intel-xeon-gold-6134 3.2 8 130 0 0
intel-xeon-gold-6134m 3.2 8 130 0 0
intel-xeon-gold-6136 3.0 12 150 0 0
intel-xeon-gold-6138 2.0 20 125 0 0
intel-xeon-gold-6138f 2.0 20 135 0 0
intel-xeon-gold-6138t 2.0 20 125 0 0
intel-xeon-gold-6140 2.3 18 140 0 0
intel-xeon-gold-6140m 2.3 18 140 0 0
intel-xeon-gold-6142 2.6 16 150 0 0
intel-xeon-gold-6142f 2.6 16 160 0 0
intel-xeon-gold-6142m 2.6 16 150 0 0
intel-xeon-gold-6144 3.5 8 150 0 0
intel-xeon-gold-6145 2.0 20 145 0 0
intel-xeon-gold-6146 3.2 12 165 0 0
intel-xeon-gold-6148 2.4 20 150 0 0
intel-xeon-gold-6150 2.7 18 165 0 26349
intel-xeon-gold-6152 2.1 22 140 0 0
intel-xeon-gold-6154 3.0 18 200 0 0
intel-xeon-gold-6161 2.2 22 165 0 0
Silver-4208 2.1 8 85 0 0
Silver-4210R 2.4 10 100 0 0
Silver-4214R 2.4 12 100 0 0
Silver-4214Y 2.2 12 85 0 0
Silver-4215R 3.2 8 130 0 0
Silver-4216 2.1 16 100 0 0
Gold-5215 2.5 10 85 0 0
Gold-5215L 2.5 10 85 0 0
Gold-5217 3.0 8 115 0 0
Gold-5218 2.3 16 125 0 0
Gold-5218R 2.1 20 125 0 0
Gold-5220 2.2 18 125 0 0
Gold-5220R 2.2 24 150 0 0
Gold-5222 3.8 4 105 0 0
Gold-6210U 2.5 20 150 0 0
Gold-6212U 2.4 24 165 0 0
Gold-6226 2.7 12 125 0 0
Gold-6226R 2.9 16 150 0 0
Gold-6230 2.1 20 125 0 0
Gold-6230R 2.1 26 150 0 0
Gold-6234 3.3 8 130 0 0
Gold-6238 2.1 22 140 0 0
Gold-6238L 2.1 22 140 0 0
Gold-6238R 2.2 28 165 0 0
Gold-6240 2.6 18 150 0 0
Gold-6240L 2.6 18 150 0 0
Gold-6240R 2.4 24 165 0 0
Gold-6240Y 2.6 18 150 0 0
Gold-6242 2.8 16 150 0 0
Gold-6242R 3.1 20 205 0 0
Gold-6244 3.6 8 150 0 0
Gold-6246 3.3 12 165 0 0
Gold-6246R 3.4 16 205 0 0
Gold-6248 2.5 20 150 0 0
Gold-6248R 3.0 24 205 0 0
Gold-6252 2.1 24 150 0 0
Gold-6254 3.1 18 200 0 0
Gold-6258R 2.7 28 205 0 0
Platinum-8256 3.8 4 105 0 0
Platinum-8260 2.4 24 165 0 0
Platinum-8260L 2.4 24 165 0 0
Platinum-8260Y 2.4 24 165 0 0
Platinum-8253 2.2 16 125 0 0
Platinum-8268 2.9 24 205 0 0
Platinum-8270 2.7 26 205 0 0
Platinum-8276 2.2 28 165 0 0
Platinum-8280 2.7 28 205 0 0
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-silver-4208 2.1 8 85 0 0
intel-xeon-silver-4210r 2.4 10 100 0 0
intel-xeon-silver-4214r 2.4 12 100 0 0
intel-xeon-silver-4214y 2.2 12 85 0 0
intel-xeon-silver-4215r 3.2 8 130 0 0
intel-xeon-silver-4216 2.1 16 100 0 0
intel-xeon-gold-5215 2.5 10 85 0 0
intel-xeon-gold-5215l 2.5 10 85 0 0
intel-xeon-gold-5217 3.0 8 115 0 0
intel-xeon-gold-5218 2.3 16 125 0 0
intel-xeon-gold-5218r 2.1 20 125 0 0
intel-xeon-gold-5220 2.2 18 125 0 0
intel-xeon-gold-5220r 2.2 24 150 0 0
intel-xeon-gold-5222 3.8 4 105 0 0
intel-xeon-gold-6210u 2.5 20 150 0 0
intel-xeon-gold-6212u 2.4 24 165 0 0
intel-xeon-gold-6226 2.7 12 125 0 0
intel-xeon-gold-6226r 2.9 16 150 0 0
intel-xeon-gold-6230 2.1 20 125 0 0
intel-xeon-gold-6230r 2.1 26 150 0 0
intel-xeon-gold-6234 3.3 8 130 0 0
intel-xeon-gold-6238 2.1 22 140 0 0
intel-xeon-gold-6238l 2.1 22 140 0 0
intel-xeon-gold-6238r 2.2 28 165 0 0
intel-xeon-gold-6240 2.6 18 150 0 0
intel-xeon-gold-6240l 2.6 18 150 0 0
intel-xeon-gold-6240r 2.4 24 165 0 0
intel-xeon-gold-6240y 2.6 18 150 0 0
intel-xeon-gold-6242 2.8 16 150 0 0
intel-xeon-gold-6242r 3.1 20 205 0 0
intel-xeon-gold-6244 3.6 8 150 0 0
intel-xeon-gold-6246 3.3 12 165 0 0
intel-xeon-gold-6246r 3.4 16 205 0 0
intel-xeon-gold-6248 2.5 20 150 0 0
intel-xeon-gold-6248r 3.0 24 205 0 0
intel-xeon-gold-6252 2.1 24 150 0 0
intel-xeon-gold-6254 3.1 18 200 0 0
intel-xeon-gold-6258r 2.7 28 205 0 0
intel-xeon-platinum-8256 3.8 4 105 0 0
intel-xeon-platinum-8260 2.4 24 165 0 0
intel-xeon-platinum-8260l 2.4 24 165 0 0
intel-xeon-platinum-8260y 2.4 24 165 0 0
intel-xeon-platinum-8253 2.2 16 125 0 0
intel-xeon-platinum-8268 2.9 24 205 0 0
intel-xeon-platinum-8270 2.7 26 205 0 0
intel-xeon-platinum-8276 2.2 28 165 0 0
intel-xeon-platinum-8280 2.7 28 205 0 0
intel-xeon-platinum-8280l 2.7 28 205 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

View File

@ -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

View File

@ -1,3 +1,4 @@
# 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

7
setup.py Normal file
View File

@ -0,0 +1,7 @@
from setuptools import setup, find_packages
setup(name='concho',
version='1.0',
packages=find_packages(),
install_requires=['lxml', 'numpy', 'matplotlib'])

10
tests/test1.py Normal file
View File

@ -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()