Merge pull request #30 from DEvil0000/improvement/configure_cTDP
Improvement/configure cTDP
This commit is contained in:
@@ -26,6 +26,9 @@ I have found that under load my CPU was not always hitting max turbo frequency,
|
|||||||
|
|
||||||
I have run **[Geekbench 4](https://browser.geekbench.com/v4/cpu/8656840)** and now I can get a score of 5391/17265! On balance_performance I can reach only 4672/16129, so **15% improvement** in single core and 7% in multicore, not bad ;)
|
I have run **[Geekbench 4](https://browser.geekbench.com/v4/cpu/8656840)** and now I can get a score of 5391/17265! On balance_performance I can reach only 4672/16129, so **15% improvement** in single core and 7% in multicore, not bad ;)
|
||||||
|
|
||||||
|
### setting cTDP (EXPERIMENTAL)
|
||||||
|
On a lot of modern CPUs from Intel one can configure the TDP up or down based on predefined profiles. This is what this option does. For a i7-8650U normal would be 15W, up profile is setting it to 25W and down to 10W. You can lookup the values of your CPU at the Intel product website.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
A stripped down version of the python module `python-periphery` is now built-in and it is used for accessing the MCHBAR register by memory mapped I/O. You also need `dbus` and `gobject` python bindings for listening to dbus signals on resume from sleep/hibernate.
|
A stripped down version of the python module `python-periphery` is now built-in and it is used for accessing the MCHBAR register by memory mapped I/O. You also need `dbus` and `gobject` python bindings for listening to dbus signals on resume from sleep/hibernate.
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ PL2_Tdp_W: 44
|
|||||||
PL2_Duration_S: 0.002
|
PL2_Duration_S: 0.002
|
||||||
# Max allowed temperature before throttling
|
# Max allowed temperature before throttling
|
||||||
Trip_Temp_C: 85
|
Trip_Temp_C: 85
|
||||||
|
# Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
|
||||||
|
cTDP: 0
|
||||||
|
|
||||||
## Settings to apply while connected to AC power
|
## Settings to apply while connected to AC power
|
||||||
[AC]
|
[AC]
|
||||||
@@ -32,6 +34,8 @@ PL2_Duration_S: 0.002
|
|||||||
Trip_Temp_C: 95
|
Trip_Temp_C: 95
|
||||||
# Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL)
|
# Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL)
|
||||||
HWP_Mode: False
|
HWP_Mode: False
|
||||||
|
# Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
|
||||||
|
cTDP: 0
|
||||||
|
|
||||||
[UNDERVOLT]
|
[UNDERVOLT]
|
||||||
# CPU core voltage offset (mV)
|
# CPU core voltage offset (mV)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ VOLTAGE_PLANES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TRIP_TEMP_RANGE = (40, 97)
|
TRIP_TEMP_RANGE = (40, 97)
|
||||||
|
C_TDP_RANGE = (0, 2)
|
||||||
|
|
||||||
power = {'source': None, 'method': 'polling'}
|
power = {'source': None, 'method': 'polling'}
|
||||||
|
|
||||||
@@ -58,6 +59,33 @@ def writemsr(msr, val):
|
|||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
# returns the value between from_bit and to_bit as unsigned long
|
||||||
|
def readmsr(msr, from_bit = 0, to_bit = 63):
|
||||||
|
if from_bit > to_bit:
|
||||||
|
print('wrong readmsr bit params')
|
||||||
|
sys.exit(1)
|
||||||
|
n = ['/dev/cpu/{:d}/msr'.format(x) for x in range(cpu_count())]
|
||||||
|
if not os.path.exists(n[0]):
|
||||||
|
try:
|
||||||
|
subprocess.check_call(('modprobe', 'msr'))
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print('[E] Unable to load the msr module.')
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
for c in n:
|
||||||
|
f = os.open(c, os.O_RDONLY)
|
||||||
|
os.lseek(f, msr, os.SEEK_SET)
|
||||||
|
val = struct.unpack('Q', os.read(f, 8))[0]
|
||||||
|
os.close(f)
|
||||||
|
extractor = int(''.join(["0"]*(63-to_bit) + ["1"]*(to_bit+1-from_bit) + ["0"]*from_bit), 2)
|
||||||
|
return (val & extractor) >> from_bit
|
||||||
|
except (IOError, OSError) as e:
|
||||||
|
if e.errno == EPERM or e.errno == EACCES:
|
||||||
|
print('[E] Unable to read from MSR. Try to disable Secure Boot.')
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def is_on_battery():
|
def is_on_battery():
|
||||||
with open(SYSFS_POWER_PATH) as f:
|
with open(SYSFS_POWER_PATH) as f:
|
||||||
@@ -65,9 +93,11 @@ def is_on_battery():
|
|||||||
|
|
||||||
|
|
||||||
def calc_time_window_vars(t):
|
def calc_time_window_vars(t):
|
||||||
|
# 0.000977 is the time unit of this CPU
|
||||||
|
time_unit = 1.0/2**readmsr(0x606, 16, 19)
|
||||||
for Y in range(2**5):
|
for Y in range(2**5):
|
||||||
for Z in range(2**2):
|
for Z in range(2**2):
|
||||||
if t <= (2**Y) * (1. + Z / 4.) * 0.000977:
|
if t <= (2**Y) * (1. + Z / 4.) * time_unit:
|
||||||
return (Y, Z)
|
return (Y, Z)
|
||||||
raise ValueError('Unable to find a good combination!')
|
raise ValueError('Unable to find a good combination!')
|
||||||
|
|
||||||
@@ -126,17 +156,26 @@ def calc_reg_values(config):
|
|||||||
regs[power_source]['MSR_TEMPERATURE_TARGET'] = trip_offset << 24
|
regs[power_source]['MSR_TEMPERATURE_TARGET'] = trip_offset << 24
|
||||||
|
|
||||||
# 0.125 is the power unit of this CPU
|
# 0.125 is the power unit of this CPU
|
||||||
PL1 = int(round(config.getfloat(power_source, 'PL1_Tdp_W') / 0.125))
|
power_unit = 1.0/2**readmsr(0x606, 0, 3)
|
||||||
|
PL1 = int(round(config.getfloat(power_source, 'PL1_Tdp_W') / power_unit))
|
||||||
Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL1_Duration_s'))
|
Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL1_Duration_s'))
|
||||||
TW1 = Y | (Z << 5)
|
TW1 = Y | (Z << 5)
|
||||||
|
|
||||||
PL2 = int(round(config.getfloat(power_source, 'PL2_Tdp_W') / 0.125))
|
PL2 = int(round(config.getfloat(power_source, 'PL2_Tdp_W') / power_unit))
|
||||||
Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL2_Duration_s'))
|
Y, Z = calc_time_window_vars(config.getfloat(power_source, 'PL2_Duration_s'))
|
||||||
TW2 = Y | (Z << 5)
|
TW2 = Y | (Z << 5)
|
||||||
|
|
||||||
regs[power_source]['MSR_PKG_POWER_LIMIT'] = PL1 | (1 << 15) | (TW1 << 17) | (PL2 << 32) | (1 << 47) | (
|
regs[power_source]['MSR_PKG_POWER_LIMIT'] = PL1 | (1 << 15) | (TW1 << 17) | (PL2 << 32) | (1 << 47) | (
|
||||||
TW2 << 49)
|
TW2 << 49)
|
||||||
|
|
||||||
|
# cTDP
|
||||||
|
try:
|
||||||
|
c_tdp_target_value = int(config.getfloat(power_source, 'cTDP'))
|
||||||
|
valid_c_tdp_target_value = min(C_TDP_RANGE[1], max(C_TDP_RANGE[0], c_tdp_target_value))
|
||||||
|
regs[power_source]['MSR_CONFIG_TDP_CONTROL'] = valid_c_tdp_target_value
|
||||||
|
except configparser.NoOptionError:
|
||||||
|
pass
|
||||||
|
|
||||||
return regs
|
return regs
|
||||||
|
|
||||||
|
|
||||||
@@ -162,7 +201,16 @@ def power_thread(config, regs, exit_event):
|
|||||||
power['source'] = 'BATTERY' if is_on_battery() else 'AC'
|
power['source'] = 'BATTERY' if is_on_battery() else 'AC'
|
||||||
|
|
||||||
# set temperature trip point
|
# set temperature trip point
|
||||||
writemsr(0x1a2, regs[power['source']]['MSR_TEMPERATURE_TARGET'])
|
if readmsr(0xce, 30, 30) != 1:
|
||||||
|
print("setting temperature target is not supported by this CPU")
|
||||||
|
else:
|
||||||
|
writemsr(0x1a2, regs[power['source']]['MSR_TEMPERATURE_TARGET'])
|
||||||
|
|
||||||
|
# set cTDP
|
||||||
|
if readmsr(0xce, 33, 34) < 2:
|
||||||
|
print("cTDP setting not supported by this cpu")
|
||||||
|
else:
|
||||||
|
writemsr(0x64b, regs[power['source']]['MSR_CONFIG_TDP_CONTROL'])
|
||||||
|
|
||||||
# set PL1/2 on MSR
|
# set PL1/2 on MSR
|
||||||
writemsr(0x610, regs[power['source']]['MSR_PKG_POWER_LIMIT'])
|
writemsr(0x610, regs[power['source']]['MSR_PKG_POWER_LIMIT'])
|
||||||
|
|||||||
Reference in New Issue
Block a user