Laptop Power Management

Power management is a critical thing to understand in laptops. It’s possible to buy an expensive laptop and end up not using it because of bad power management practices.

Also, not taking care of the thermals on a laptop will lead to less laptop life, shorter battery, and unpleasant heat generation.

Cooling Pad

Get any cheap amazon cooling pad from amazon. Here is a decent $30 one with lots of reviews. https://amzn.to/3SV0Y0l

Redo Thermal Paste

By default, almost any and every laptop has excessive thermal compound which leads to higher temperatures. Simply removing the excess compound and apply just a drop of “good” thermal paste will drop temps by about 10 Celsius.

Here is an example of excess factory thermal compound application:

termal

Adjust Max CPU Frequency

Set your CPU governor to the maximum power it can use. I NEVER use a 100% of the CPU and in most instances rarely break above 50% of its total power. Typically, around 2 GHz is perfect for my day-to-day work. When working on battery, I put this down to 25% on a beefy laptop to conserve most of its power. On weaker laptops, you’d probably double these numbers. 100% on AC Power and 50% on Battery.

Linux - cpupower

Required Dependencies

Install the following packages

  • cpupower
  • acpi-support
  • acpid
  • acpi

Example Install:

apt-get install acpi-support acpid acpi linux-cpupower cpufrequtils

Setting Min/Max Power Example

cpupower frequency-set -g powersave -d 0.8G -u 1.2G

Check Frequency

cpufreq-info

Disable Turbo

Turbo will draw more power and reduce battery life while increasing CPU temperature.

Run the following from a root user:

echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo

Turn off CPU Cores

The more CPU cores that your laptop uses, the power it will draw. On higher end laptops you can see as big as a 75% savings and extend the battery life x3.

Taking a CPU core offline (Note: you can’t offline cpu0)

echo 0 > /sys/devices/system/cpu/cpu#/online

To disable cores above 4 threads, run the following in a bash script:

for ((i=4;i<$(nproc);i++)); do echo 0 > /sys/devices/system/cpu/cpu$i/online; done

Use CPULIMIT to Constrain Apps

Install

sudo apt install cpulimit

Adjust EXEC commands in .desktop

cpulimit --limit 70 /usr/bin/google-chrome-bin

Note: This would limit CPU usage for Google Chrome to 70%

Linux - adjust GPU power

There are two methods for adjusting the power usage your GPU uses. I personally use both depending on the laptop, but eGPU’s will eat up a battery laptop in no time.

Manually adjust MHz of GPU

Set the following variables:

  • /sys/class/drm/card0/gt_max_freq_mhz
  • /sys/class/drm/card0/gt_boost_freq_mhz

Using cat to display current MHz and then simply echo the new lower value to these variables.

Example:

echo "1000" > /sys/class/drm/card0/gt_max_freq_mhz

Turning off GPU with Prime-select

While there are technically 3 options, I recommend only using NVIDIA and intel, as on-demand doesn’t work very well.

Turning off GPU with Envycontrol

Cool python project that rebuilds the init system to exclude nvidia.

https://github.com/geminis3/EnvyControl

Windows - GUI Power Management

Launch the GUI power panel with Start ⇾ Run powercfg.cpl

win-power

Then change the Min / Max CPU Power. I typically set these to 5% Min and 50% Max, but on weaker laptops you can set these higher.

Windows - AutoHotkey Script

This script sets the max CPU state when plugged in to the wall (AC Power) and when it runs on battery (DC Power).

PowerWriteProcessorStateValueIndex(50, "AC")
PowerWriteProcessorStateValueIndex(25, "DC")
ExitApp

;---------------------
PowerWriteProcessorStateValueIndex(Max := "", Mode := "") {
	l:=PowerEnumeratePlans(ActivePlan), VarSetCapacity(GUID_ACTIVE_POWER_SAVING, 16, 0), VarSetCapacity(GUID_PROCESSOR_SETTINGS_SUBGROUP, 16, 0)
	, DllCall("Ole32.dll\CLSIDFromString", "WStr", "{" ActivePlan.GUID "}", "Ptr", &GUID_ACTIVE_POWER_SAVING), VarSetCapacity(GUID_PROCESSOR_THROTTLE_MAXIMUM, 16, 0)
	, DllCall("Ole32.dll\CLSIDFromString", "WStr", "{54533251-82be-4824-96c1-47b60b740d00}", "Ptr", &GUID_PROCESSOR_SETTINGS_SUBGROUP)
	, DllCall("Ole32.dll\CLSIDFromString", "WStr", "{BC5038F7-23E0-4960-96DA-33ABAF5935EC}", "Ptr", &GUID_PROCESSOR_THROTTLE_MAXIMUM)
	, DllCall("PowrProf.dll\PowerWrite" Mode "ValueIndex", "Ptr", 0, "Ptr", &GUID_ACTIVE_POWER_SAVING
	, "Ptr", &GUID_PROCESSOR_SETTINGS_SUBGROUP, "Ptr", &GUID_PROCESSOR_THROTTLE_MAXIMUM, "UInt", Max, "UInt")
	for k, v in l
		if !(v.Default) {
			RunWait, % ComSpec " /c powercfg /s " v.GUID,, Hide
			break
		} RunWait, % ComSpec " /c powercfg /s " ActivePlan.GUID,, Hide
}

;------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

;enumerates all power plans
;for k, v in PowerEnumeratePlans()
;	MsgBox % "#" k "`nGUID: " v.GUID "`Name: " v.Name "`nDefault?: " v.Default
PowerEnumeratePlans(ByRef ActivePlan := "") {
	List := {}
	for k, v in StrSplit(CMD("powercfg /l"), ":", A_Space A_Tab)
		if (A_Index>1) {
			Info := {}, Info.GUID := SubStr(Trim(v), 1, 36), Info.Default := !!InStr(v, "*")
			, Info.Name := Trim(StrSplit(SubStr(v, InStr(v, "(")+1), ")")[1]), List.Push(Info)
			if (Info.Default)
				ActivePlan := Info
		} return List
}

;executes cmd command and return the result
CMD(CMD := "", Encoding := "UTF-8", ByRef ExitCode := "") {
	DllCall("Kernel32.dll\CreatePipe", "PtrP", hReadPipe, "PtrP", hWritePipe, "Ptr", 0, "UInt", 0)
	, DllCall("Kernel32.dll\SetHandleInformation", "Ptr", hWritePipe, "UInt", 1, "UInt", 1)
	, Size := VarSetCapacity(STARTUPINFO, A_PtrSize=4?68:104, 0), NumPut(Size, STARTUPINFO, 0, "UInt")
    , NumPut(0x100, STARTUPINFO, A_PtrSize=4?44:60, "UInt"), NumPut(hStdInRd, STARTUPINFO, A_PtrSize=4?56:80, "Ptr")
    , NumPut(hWritePipe, STARTUPINFO, A_PtrSize=4?60:88, "Ptr"), NumPut(hWritePipe, STARTUPINFO, A_PtrSize=4?64:96, "Ptr")
	, pi := CreateProcess(, ComSpec " /c " CMD,,,, 0x08000000,,, &STARTUPINFO)
	, DllCall("Kernel32.dll\CloseHandle", "Ptr", hWritePipe), Size := VarSetCapacity(Buff, 4095)
	while DllCall("Kernel32.dll\ReadFile", "Ptr", hReadPipe, "Ptr", &Buff, "UInt", Size, "PtrP", Bytes, "Ptr", 0)
        OutputVar .= StrGet(&Buff, Bytes, Encoding)
	DllCall("Kernel32.dll\CloseHandle", "Ptr", pi.handle), DllCall("Kernel32.dll\CloseHandle", "Ptr", hReadPipe)
	return OutputVar, DllCall("Kernel32.dll\GetExitCodeProcess", "Ptr", pi.handle, "UIntP", ExitCode)
} CreateProcess(ApplicationName := "", ByRef CommandLine := "", ProcessAttributes := 0, ThreadAttributes := 0, InheritHandles := true, CreationFlags := 0x08000000, Environment := 0, CurrentDirectory := "", StartupInfo := 0) {
	VarSetCapacity(PROCESS_INFORMATION, A_PtrSize=4?16:24, 0)
	, r := DllCall("Kernel32.dll\CreateProcess", "Ptr", ApplicationName=""?0:&ApplicationName, "Ptr", CommandLine=""?0:&CommandLine
	, "Ptr", ProcessAttributes, "Ptr", ThreadAttributes, "Int", !!InheritHandles, "UInt", CreationFlags, "Ptr", Environment, "Ptr", CurrentDirectory=""?0:&CurrentDirectory
	, "Ptr", StartupInfo, "Ptr", &PROCESS_INFORMATION)
	return r?{HANDLE: NumGet(PROCESS_INFORMATION, 0, "Ptr"), hThread: NumGet(PROCESS_INFORMATION, A_PtrSize, "Ptr")
	, ID: NumGet(PROCESS_INFORMATION, A_PtrSize=4?8:16, "Ptr"), ThreadID: NumGet(PROCESS_INFORMATION, A_PtrSize=4?12:24, "Ptr")}:0, ErrorLevel := !r
}

Walkthrough Video