Spicy's Ultimate Computer Utility Version 4.0

Version 4.0 of my program is out! You may remember my messy, old code from here.

Anyway, what this program does is several things:

• Calculate CPU and GPU processing power in TFLOPS (current standard for measuring processing power in computing)

• Calculate total processing power in my own optimized score unit that factors everything in the computer into the equation.

• Calculate total estimated load wattage and recommends a power supply.

• Calculate energy costs over time.

• Some more things too.

from functools import partial

from termcolor import colored

magenta = partial(colored,
color='magenta')
cyan = partial(colored,
color='cyan')
yellow = partial(colored,
color='yellow')

def run():
pc_type = start()
cpu_tdp, ram_type, cpu_score, cpu_avg_tflops = cpu(pc_type)
ram_score, ram_tdp = ram(ram_type)
gputdp, gpu_score, gpu_tflops = gpu(pc_type)
drive_score, drive_type = drive1()
hddtdp, ssdtdp = drive2(drive_score)
bottleneck(pc_type=pc_type,
cpu_score=cpu_score,
drive_score=drive_score,
drive_type=drive_type,
gpu_score=gpu_score,
ram_score=ram_score)
total_tdp = final(pc_type=pc_type,
cpu_score=cpu_score,
cpu_tdp=cpu_tdp,
drive_score=drive_score,
gpu_score=gpu_score,
gputdp=gputdp,
hddtdp=hddtdp,
ram_score=ram_score,
ram_tdp=ram_tdp,
ssdtdp=ssdtdp,
cpu_avg_tflops=cpu_avg_tflops,
gpu_tflops=gpu_tflops)
energy_cost_calc(total_tdp=total_tdp)
again()
thanks()

def start():
print(colored("'." * 70,
color='white',
print(magenta("* Welcome to Spicy's Ultimate Computer Utility *"))
print(magenta("* Version Alpha 4.0 *"))
print(magenta("** This program is still in alpha stage "
"and isn't perfect **"))
print(magenta("If you are unsure about one of the questions, "
"look up the specs of the part on a website "
"like PCPartPicker or TechPowerUP"))
print(magenta("** Make sure to pay attention to the units "
"specified in the questions. **"))
print(magenta("** All results/scores are theoretical estimates. **"))
return float(input(cyan("Is your system a laptop or desktop? "
"1 for laptop, 2 for desktop.")))

def cpu(pc_type):
l1_factor = 100 #The smaller this is, the more L1 cache matters
l3_factor = .3 #The larger this is, the more L3 cache matters
name = input(cyan("What is the name of your processor?"))
tdp = float(input(cyan(f"What is the TDP of the {name}? (in W)")))
cores_count = float(input(cyan(f"How many cores does the {name} have?")))
f"does the {name} have?")))
l1_cache = float(input(cyan(f"How much L1 cache (in total) "
f"does the {name} have in KB?")))
l2_cache = float(input(cyan(f"How much L2 cache (in total) "
f"does the {name} have in MB?")))
l3_cache = float(input(cyan(f"How much L3 cache (in total) "
f"does the {name} have in MB?")))
clock_speed = float(input(cyan(f"What is the clock speed (in GHz) "
f"of the {name}?")))
fabrication = float(input(cyan(f"What is the fabrication process "
f"of the {name} in nm?")))
ram_type = float(input(cyan(f"What type of RAM does the {name} use? "
f"Enter 3 for DDR3, 4 for DDR4, etc.")))
score = ((cores_count + threads_count + clock_speed + ram_type + l2_cache
+ (l1_cache / l1_factor) + (l3_factor * l3_cache))
/ fabrication) * pc_type
#the 2 is for the average of cores and threads
#the first 1000000000 is to convert GHz to Hz
#the second 1000000000 is to convert FLOPS to GFLOPS
#the 8 is for 8 single precision floating point operations per cycle
#the 4 is for 4 double precision floating point operations per cycle
print(yellow(f"** The {name} has a score of {round(score, 5)}. **"))
print(yellow(f"** The {name} is capable of {round(cpu_sp_gflops, 2)} single precision GFLOPS. **"))
print(yellow(f"** The {name} is capable of {round(cpu_dp_gflops, 2)} double precision GFLOPS. **"))
cpu_avg_tflops = ((cpu_sp_gflops+cpu_dp_gflops)/2)/1000
return tdp, ram_type, score, cpu_avg_tflops

def ram(ram_type):
ram_tdp_factor = 4 #1 RAM stick draws around 4W
dimm_speed_factor = 500 #The smaller this is, the more speed matters
ram_equation_factor = 150 #The smaller this is, the less RAM score matters overall
amount = float(input(cyan("How much RAM does your system have "
"(in GB?)")))
slowest_dimm_speed = float(input(cyan("What is the speed of your slowest "
"installed DIMM? (in MHz)")))
dimms_count = (float(input(cyan("How many DIMMs do you have installed?")))
* ram_tdp_factor)
score = ((ram_type + (slowest_dimm_speed / dimm_speed_factor)) * amount) / ram_equation_factor
print(yellow(f"** Your RAM has a score of {round(score, 5)}. **"))
return score, dimms_count

def gpu(pc_type):
name = input(cyan("What is the name of your GPU?"))
memory_type = int(input(cyan(f"What type of memory does the {name} use? "
f"0 for GDDR, 1 for HBM")))
tdp = float(input(cyan(f"What is the TDP of the {name}? (in W)")))

if memory_type == 0:
cores_factor = 400 #The smaller this is, the more cores matter
tmus_factor = 25 #the smaller this is, the more TMUs matter
rops_factor = 10 #the smaller this is, the more ROPs matter
memsize_factor = 1000 #the smaller this is, the more VRAM amount matters
buswidth_factor = 30 #the smaller this is, the more bus width matters
clockspeed_factor = 2 #the larger this is, the more clockspeed matters
memspeed_factor = 1000 #the smaller this is, the more VRAM speed matters
gpu_equation_factor = 25 #the smaller this is, the more GPU score matters overall
cores_count = float(input(cyan(f"How many cores "
f"does the {name} have?")))
tmus_count = float(input(cyan(f"How many TMUs "
f"does the {name} have?")))
rops_count = float(input(cyan(f"How many ROPs "
f"does the {name} have?")))
memory_size = float(input(cyan(f"How much memory (in MB) "
f"does the {name} have?")))
memory_type = float(input(cyan(f"What type of memory "
f"does the {name} have? "
f"Enter 3 for GDDR3, 5 for GDDR5, "
f"and 8 for GDDR5X.")))
# GDDR5X is 8 to account for its additional data rate
bus_width = float(input(cyan(f"What is the bus width "
f"of the {name}?")))
clock_speed = float(input(cyan(f"What is the clock speed "
f"of the {name} in GHz?")))
memory_speed = float(input(cyan(f"What is the effective memory speed "
f"of the {name} in MHz?")))
fabrication = float(input(cyan(f"What is the fabrication process "
f"of the {name} in nm?")))
score = ((((cores_count / cores_factor) + (tmus_count / tmus_factor) + (rops_count / rops_factor)
+ (memory_size / memsize_factor) + (bus_width / buswidth_factor)
+ (clock_speed * clockspeed_factor) + (memory_speed / memspeed_factor))
* ((memory_type / fabrication) / gpu_equation_factor))) * pc_type

if memory_type == 1:
cores_factor = 400 #The smaller this is, the more cores matter
tmus_factor = 25 #the smaller this is, the more TMUs matter
rops_factor = 10 #the smaller this is, the more ROPs matter
memsize_factor = 1000 #the smaller this is, the more VRAM amount matters
buswidth_factor = 250 #the smaller this is, the more bus width matters
clockspeed_factor = 2 #the larger this is, the more clockspeed matters
memspeed_factor = 150 #the smaller this is, the more VRAM speed matters
gpu_equation_factor = 25 #the smaller this is, the more GPU score matters overall
memtypfabcombo_factor = 3 #the larger this is, the more memory type/fabrication matters
cores_count = float(input(cyan(f"How many cores "
f"does the {name} have?")))
tmus_count = float(input(cyan(f"How many TMUs "
f"does the {name} have?")))
rops_count = float(input(cyan(f"How many ROPs "
f"does the {name} have?")))
memory_size = float(input(cyan(f"How much memory (in MB) "
f"does the {name} have?")))
memory_type = float(input(cyan(f"What type of memory "
f"does the {name} have? "
f"Enter 1 for HBM, 2 for HBM2, etc.")))
bus_width = float(input(cyan(f"What is the bus width "
f"of the {name}?")))
clock_speed = float(input(cyan(f"What is the clock speed "
f"of the {name} in GHz?")))
memory_speed = float(input(cyan(f"What is the effective memory speed "
f"of the {name} in MHz?")))
fabrication = float(input(cyan(f"What is the fabrication process "
f"of the {name} in nm?")))
score = (((cores_count / cores_factor) + (tmus_count / tmus_factor) + (rops_count / rops_factor)
+ (memory_size / memsize_factor) + (bus_width / buswidth_factor)
+ (clock_speed * clockspeed_factor) + (memory_speed / memspeed_factor))
* ((((memory_type / fabrication))*memtypfabcombo_factor) / gpu_equation_factor)) * pc_type
gpu_tflops = ((rops_count * 4 * 16) * clock_speed)/1000
#The 4 is for each compute unit (ROP) has 4 SIMD execution units
#The 16 is for each SIMD unit, which is 16 ALUs wide per unit
#The 1000 is to convert GFLOPS to TFLOPS
print(yellow(f"** The {name} has a score of {round(score, 5)}. **"))
print(yellow(f"** The {name} is capable of {round(gpu_tflops, 2)} TFLOPS. **"))

return tdp, score, gpu_tflops

def drive1():
drive_equation_factor = 30 #the smaller this is, the more drive score matters overall
drive_type = float(input(cyan("Is your boot drive an HDD or SSD? "
"Enter 1 for HDD, 2 for SSD.")))
drive_interface = float(input(cyan("What interface does your boot drive use? "
"1 for SATA, 2 for IDE/PATA.")))
#need to implement this
if drive_type == 1:
rpm_factor = 1000 #the smaller this is, the more HDD RPM matters
hdd_rpm = float(input(cyan("What is the RPM of your HDD?")))
hdd_cache = float(input(cyan("How much buffer cache (in MB) does your HDD have?")))
hdd_cache_factor = 25 #the larger this is, the more HDD buffer cache size matters
drive_free = float(input(cyan("How much storage is available "
"(not filled) on your boot drive "
"(in GB)")))
drive_total = float(input(cyan("What is the total amount of storage "
"on your boot drive (in GB)")))
percent_free = (drive_free / drive_total) * 100
print(yellow(f"* Your boot drive is "
f"{round(percent_free, 3)}% free. *"))
drive_score = ((((1 / percent_free) * 100) * (hdd_rpm + (hdd_cache * hdd_cache_factor) / rpm_factor)) / drive_equation_factor) / drive_interface
if drive_type == 2:
ssd_factor = 5 #the larger this is, the more drive score will increase if SSD is boot drive
drive_free = float(input(cyan(f"How much storage is available "
f"(not filled) on your boot drive "
f"(in GB)")))
drive_total = float(input(cyan("What is the total amount of storage "
"on your boot drive (in GB)")))
percent_free = (drive_free / drive_total) * 100
print(yellow(f"* Your boot drive is "
f"{round(percent_free, 3)}% free. *"))
drive_score = (((((1 / percent_free) * 100) * ssd_factor) * drive_type)
/ drive_equation_factor) / drive_interface

return drive_score, drive_type

def drive2(drive_score):
ssdtdp = float(input(cyan("How many SSDs do you have "
ssdtdp *= 3.25  # 1 SSD draws this much watts (the *= thing means ssdtdp = ssdtdp*3.25)
hddtdp = float(input(cyan(f"How many HDDs do you have "
hddtdp *= 8  # 1 HDD draws this much watts
print(yellow(f"** Your boot drive's score is "
f"{round(drive_score, 5)}. **"))
return hddtdp, ssdtdp

def final(pc_type,
cpu_score,
gpu_score,
drive_score,
ram_score,
cpu_tdp,
gputdp,
ram_tdp,
hddtdp,
ssdtdp,
gpu_tflops,
cpu_avg_tflops):
cpu_gpu_tdp_factor = 0.8 #the larger this is, the more to remove from CPU and GPU TDP
#(because they don't reach full spec)

mainboard_tdp = input(cyan("Does your mainboard support overclocking? "
"(Y/N)"))

if mainboard_tdp == "N":
mainboard_tdp = 32.5

if mainboard_tdp == "Y":
mainboard_tdp = 62.5

if pc_type == 2:
wifi_tdp = input(cyan("Do you have a wireless adapter installed?"
"(Y/N)"))
sc_tdp = input(cyan("Do you have a sound card installed? (Y/N)"))

if wifi_tdp == "Y":
wifi_tdp = 2

if wifi_tdp == "N":
wifi_tdp = 0

if sc_tdp == "Y":
sc_tdp = 10

if sc_tdp == "N":
sc_tdp = 0

if pc_type == 1:
wifi_tdp = 1
sc_tdp = 3
# both of these are assumptions, I have no idea how much W laptop WiFi cards or sound cards use
final_score = (cpu_score + gpu_score + drive_score + ram_score)
final_tflops = cpu_avg_tflops+gpu_tflops
dvdtdp = float(input(cyan("How many optical drives "
"do you have installed?")))
# 1 ODD draws around 20 watts, I'm assuming laptop ODDs use half of that.
#need to implement support for laptops without ODD
dvdtdp = dvdtdp * 10 * pc_type

if pc_type == 2:
fantdp = float(input(cyan("How many case fans do you have installed? "
"(counting CPU cooler)")))
fantdp *= 4  # 1 120mm fan draws around 4 watts

if pc_type == 1:
fantdp = 0.5 #I will assume that laptops' cooling solutions use 0.5 watts total

usbtdp = float(input(cyan("How many USB ports does your computer have? "
"(in total)")))
usbtdp *= 5  # USB 3.0 can pull 5W
# Estimated max load TDP equation
total_tdp = (((cpu_tdp + gputdp) * cpu_gpu_tdp_factor) + ram_tdp + hddtdp + ssdtdp
+ dvdtdp + fantdp + usbtdp + sc_tdp + wifi_tdp) + mainboard_tdp
# max spec TDP equation
psu_factor = 50 #The larger this is, the more "extra" watts to recommend for the PSU
#since a PSU should never reach full spec
recommended_psu = (cpu_tdp + gputdp + ram_tdp + hddtdp + ssdtdp + dvdtdp + fantdp
+ usbtdp + mainboard_tdp + wifi_tdp + sc_tdp + psu_factor)
print(magenta("Calculating final score..."))
print(yellow("** Your final score is... **"))
print(round(final_score, 5))
print(yellow(f"** Your system is capable of {final_tflops} TFLOPS. **"))
print(f"{round(total_tdp, 1)} Watts")
print(yellow(f"** I would recommend using a power supply of at least "
f"{int(round(recommended_psu, -1))} watts. **"))

def energy_cost_calc(total_tdp):
hrscalc = input(cyan("Would you also like to factor in "
if hrscalc == "Y":
hrs = float(input(cyan("On average, how many hours do you use "
price = float(input(cyan("What is the price per kWh "
"where you live? (in dollars)")))
ktdp = total_tdp / 1000  # TDP in kW
dailycost = hrs * price * ktdp
hourcost = dailycost / 24
weeklycost = dailycost * 7
monthlycost = dailycost * 30.42 #an average month has 30.42 days
yearlycost = dailycost * 365
print(yellow(f"Your cost is ${round(hourcost, 2)} per hour, " f"${round(dailycost, 2)} per day, "
f"${round(weeklycost, 2)} per week, " f"${round(monthlycost, 2)} per month, "
f"and \${round(yearlycost, 2)} per year."))

def bottleneck(pc_type,
gpu_score,
ram_score,
drive_score,
cpu_score,
drive_type):
print(yellow("Calculating bottleneck..."))
bram_factor = 4
bdrive_factor = 5
if pc_type == 1:  # laptop
bgpu_factor = 3.5
bcpu_factor = 2
bgpus = gpu_score * bgpu_factor
brams = ram_score * bram_factor
bhds = drive_score * bdrive_factor
bcpus = cpu_score * bcpu_factor

if pc_type == 2:  # desktop
bgpu_factor = 1.75
bgpus = gpu_score * bgpu_factor
brams = ram_score * bram_factor
bhds = drive_score * bdrive_factor
bcpus = cpu_score

if bgpus <= brams and bgpus <= bhds and bgpus <= bcpus:
gpub = 1
cpub = 0
ramb = 0
hdb = 0
if bcpus <= brams and bcpus <= bhds and bcpus <= bgpus:
cpub = 1
gpub = 0
ramb = 0
hdb = 0
if brams <= bcpus and brams <= bhds and brams <= bgpus:
ramb = 1
cpub = 0
gpub = 0
hdb = 0
if bhds <= brams and bhds <= bcpus and bhds <= bgpus:
hdb = 1
cpub = 0
ramb = 0
gpub = 0
if gpub == 1:
print(yellow("You could improve your GPU score "
"by overclocking or replacing your GPU."))

if cpub == 1:
print(yellow("You could improve your CPU score "
"by overclocking or replacing your CPU."))

if ramb == 1:
print(yellow("You could improve your RAM score "
"by overclocking, replacing, "
"or installing more or faster RAM."))

if hdb == 1:

if drive_type == 1:
print(yellow("You could improve your boot disk score "
"by replacing your HDD with a faster HDD or an SSD, "
"or by freeing up space."))

if drive_type == 2:
print(yellow("You could improve your boot disk score "
"by freeing up space."))

def thanks():
print(magenta("Thank you for using Spicy's Ultimate Computer Utility!"))
color='green'))

def again():
again = input(cyan("Do you want to use the calculator again? Y/N"))
if again == 'Y':
start()

if __name__ == '__main__':
run()
else:
run()


def again():
again = input(cyan("Do you want to use the calculator again? Y/N"))
if again == 'Y':
start()


You probably meant to call run() rather than start(), because this code doesn't make the program repeat as intended.

But more importantly, functions should not be treated as goto labels! Calling start() or run() is therefore inappropriate. Answering "Y" would lead to unnecessary deepening of the call stack. If the program might loop, then you should write it as a loop:

def again():
return 'Y' == input(cyan("Do you want to use the calculator again? Y/N"))

def main():
while True:
run()
if not again():
break
thanks()


if __name__ == '__main__':
run()
else:
run()


The else block defeats the whole purpose of the if __name__ == '__main__' test.

• To expand on "functions should not be treated as goto labels!" There is also the problem that at some point there will be a stack overflow exception, if the user keeps running the program. Mar 9, 2018 at 8:54

Instead of a long series of if blocks to find out the bottleneck, why not use min?

if bgpus <= brams and bgpus <= bhds and bgpus <= bcpus:
gpub = 1
cpub = 0
ramb = 0
hdb = 0
if bcpus <= brams and bcpus <= bhds and bcpus <= bgpus:
cpub = 1
gpub = 0
ramb = 0
hdb = 0
if brams <= bcpus and brams <= bhds and brams <= bgpus:
ramb = 1
cpub = 0
gpub = 0
hdb = 0
if bhds <= brams and bhds <= bcpus and bhds <= bgpus:
hdb = 1
cpub = 0
ramb = 0
gpub = 0


becomes:

bottleneck = min(bhds, brams, bcpus, bgpus)
if bhds == bottleneck:
...


if pc_type == 2:
wifi_tdp = input(cyan("Do you have a wireless adapter installed?"
"(Y/N)"))
sc_tdp = input(cyan("Do you have a sound card installed? (Y/N)"))

if wifi_tdp == "Y":
wifi_tdp = 2

if wifi_tdp == "N":
wifi_tdp = 0


every boolean type question, you want the answer to be Y and N. I hate having to type in uppercase, and generally, just press enter (which is default assumed to be y for me in apt install etc.). You might change it to

input_value in 'yY'  # or in 'nN'


I ran the program with my system specs, and towards the end, in final; I received:

Does your mainboard support overclocking? (Y/N)y
Do you have a wireless adapter installed?(Y/N)n
Do you have a sound card installed? (Y/N)y
How many optical drives do you have installed?0
How many case fans do you have installed? (counting CPU cooler)7
How many USB ports does your computer have? (in total)12
Traceback (most recent call last):
File "189181.py", line 436, in <module>
run()
File "189181.py", line 37, in run
gpu_tflops=gpu_tflops)
File "189181.py", line 313, in final
+ dvdtdp + fantdp + usbtdp + sc_tdp + wifi_tdp) + mainboard_tdp
TypeError: unsupported operand type(s) for +: 'float' and 'str'


You can abstract the user-input in a separate function:

def get_input(prompt, value_type=float, strict=True):
prompt = cyan(prompt + '> ')
while True:
value = input(prompt)
try:
value = value_type(value)
return value
except ValueError as e:
if strict is True:
continue
raise e  # is not strict


which will keep asking user for input (if strict is True`) until the input type is valid.