I recently spent some time tuning the power profiles on my laptop running CachyOS and KDE Plasma. I wanted the three buttons in KDE’s power menu to actually mean something:

  • power-saver should make the laptop quiet and conservative on battery;
  • balanced should behave like the normal default;
  • performance should feel immediate, without secretly keeping power-saving limits enabled.

I expected the usual result: power-saver would use the least energy, performance the most, and balanced would sit somewhere in between.

Then I encoded the same video with libx264 on all three profiles. Power-saver took about 38 seconds and used roughly twice as much energy as balanced. Performance finished in about 14 seconds and used the least.

That result is specific to this laptop and this workload, but it changed the question I was trying to answer. “Which profile uses less energy?” is not useful until I say whether I mean finishing a task or keeping the laptop running through a long session.

A small disclaimer: I am a Linux enthusiast, not a power-management expert. Before starting this, I did not understand many of the lower-level details behind CPU scaling, PCI runtime power management, scheduler behaviour, or how the individual components interact.

I spent a lot of time reading documentation, searching for other people’s experiences, and using AI to help navigate the unfamiliar parts. I then checked those assumptions against the actual configuration and measurements on my own laptop.

This is not a universal tuning guide. It is a record of what I learned while solving a specific problem on one machine.

Starting point

The laptop is a ThinkPad T14 Gen. 4 with an Intel Core i7-1355U and integrated graphics, running CachyOS with the CachyOS kernel and BORE scheduler on KDE Plasma Wayland.

I deliberately decided to build everything around power-profiles-daemon rather than TLP or auto-cpufreq.

KDE already uses power-profiles-daemon for its profile switcher, and on this laptop it already controls things such as CPU Energy Performance Preference (EPP) and Lenovo’s platform_profile.

There was little point in replacing that mechanism with another daemon and creating competing sources of truth.

Instead, I split the tuning into two kinds of settings.

The first kind can directly change how the machine behaves under load:

  • CPU frequency limits;
  • Turbo Boost;
  • GPU frequency limits;
  • Wi-Fi power saving.

These have to follow the currently selected profile.

The second kind mostly changes what hardware does while it is idle:

  • PCIe ASPM;
  • PCI runtime power management;
  • HDA codec power saving;
  • USB autosuspend.

These do not impose a CPU or GPU performance ceiling, so I keep them global. They are not completely free, though: for example, runtime power management on Ethernet disables Wake-on-LAN on this machine, but I don’t need it anyway.

Making the profiles actually different

power-profiles-daemon doesn’t provide hooks for arbitrary commands when a profile changes, so I added a small service that watches the active profile over D-Bus and applies the corresponding settings.

The important part is that it applies the complete state of each profile rather than only setting values when entering power-saver.

That means balanced explicitly restores its defaults. Otherwise switching from power-saver to performance can leave one of the old restrictions behind.

The real logic is super boring:

power-saver → turbo off, CPU/GPU ceilings, Wi-Fi power save on
balanced    → restore CPU/GPU defaults, turbo on, Wi-Fi power save off
performance → CPU/GPU floors, turbo on, Wi-Fi power save off

The resulting profiles look roughly like this:

Settingpower-saverbalancedperformance
CPU Turbooffonon
CPU max performance60%100%100%
CPU min performance (requested)0%0%25%
GPU max frequency500 MHzRP0RP0
GPU min frequencyRPnRPn400 MHz
Wi-Fi power saveonoffoff

The 60% CPU ceiling in power-saver is intentional. I wanted this profile to be more than just “slightly more efficient”: it is also a quiet and thermally conservative mode. I don’t want to be that person with a personal jet engine in a coffee shop.

The performance profile goes in the opposite direction. Its frequency floors are there specifically to reduce ramp-up latency and make the machine feel more immediate.

The first useful result: idle power

Before touching performance limits, I looked at the things that were unnecessarily keeping devices awake.

A few changes were straightforward:

  • HDA audio codec power saving;
  • PCIe ASPM;
  • PCI runtime power management for Wi-Fi, Ethernet and NVMe.

Some other settings were already sensible in CachyOS. USB autosuspend was already enabled, nmi_watchdog was already disabled, and some kernel defaults were already tuned.

The snapshots below were collected while I was iterating on the setup. They are not a controlled A/B comparison: the background desktop activity was not identical in every measurement.

The lowest later idle measurement was 5.51 W, compared with roughly 9.2 W in the original snapshot. This is a useful end result, but it is not evidence that PCI runtime power management alone saved 3.7 W.

All measurements were on battery with roughly 55% brightness, Wi-Fi connected, and a short pause for the desktop to settle.

I used power-idle-bench.sh, which samples the battery’s power_now value and prints the average, minimum and maximum power draw together with a snapshot of the active power settings.

The comparison looked like this:

Measurement snapshotProfileAverage
Stockbalanced9.37 W
Idle tuningbalanced9.20 W
+ Turbo off in saverpower-saver8.12 W
Later tuned setupbalanced5.51 W
+ CPU/GPU limitspower-saver6.51 W
+ performance floorsperformance6.13 W

PCI runtime power management was part of the later setup, and it may have helped. I did not isolate it well enough to claim how much it contributed. The defensible conclusion is simply that the laptop can idle around 5.5 W in this configuration.

Then I tested actual work

My first test was deliberately simple: perform the same amount of work on every profile.

I used a fixed video encoding task with libx264 and measured the energy consumed until the task was finished.

The power-load-bench.sh script encodes a fixed 60-second synthetic 1080p30 video, samples the battery’s power_now value once per second, and calculates Wh from the average power and elapsed time.

If I instead measured power used during a fixed 60 seconds, the slower power-saver profile would simply do less work. That would answer a different question.

For a fixed task, the relevant metric is:

energy per task = Wh consumed until the task is finished.

And the result was almost the opposite of what I expected.

ProfileTimeEnergy
power-saver~38 s~0.095–0.139 Wh
balanced~22–23 s~0.051–0.063 Wh
performance~14–15 s~0.046–0.049 Wh

Across two independent sessions, the relationship was consistent.

Power-saver used roughly twice as much energy per task as balanced.

Performance was actually the most energy-efficient, at roughly 0.8–0.9× the energy of balanced.

These are measurements of the complete profiles, not a clean CPU-frequency experiment. Turbo, CPU and GPU limits, Wi-Fi power saving and the scheduler mode all change together.

The cost of waiting

The laptop has a fairly large baseline power consumption.

At idle, the whole platform consumes roughly 5.5 W under these conditions: display, memory, Wi-Fi, uncore and everything else.

Think of that as an “idle rent” that I pay for every second the laptop is awake.

Power-saver takes about 15 seconds longer than balanced to finish the encode. Those seconds still cost roughly 5.5 W for the display, memory, Wi-Fi, uncore and the rest of the platform — before counting the CPU itself.

Performance consumes more power while working, but finishes much sooner and can return to idle. This is the usual race-to-idle effect. On this particular machine it outweighed the savings from making the CPU slower for this workload.

So for a task like:

“Encode this video and then I’m done.”

power-saver is actually the worst choice.

A different question: ten minutes with bursts and pauses

The previous test doesn’t really resemble how I use a laptop in a cafe.

I don’t usually encode something continuously for 10 minutes.

I write something, compile it, read documentation, switch to a browser, answer a message, stare at the screen for a while, then do another small burst of work.

So I created a ten-minute test. Every minute consists of encoding the same 20-second synthetic 1080p30 video, followed by a pause until the end of the minute.

This is both a fixed-work and fixed-time-window test: every profile encodes the same ten short videos, but faster profiles get back to idle sooner after each one. It answers a different, practical question: how much battery does the laptop use during a time-bounded session with repeated tasks and idle time?

The complete benchmark is power-mixed-bench.sh. It samples the battery’s power_now value once per second for the full ten-minute window.

For that test, the result flipped:

ProfileAverage powerEnergy / 10 min
power-saver6.91 W1.152 Wh
balanced7.84 W1.307 Wh
performance12.17 W2.028 Wh

Power-saver used the least energy in this particular test: about 12% less than balanced, while performance used 55% more.

Why?

Because most of every minute is now idle.

The performance profile has deliberately aggressive minimum frequencies:

  • CPU minimum performance around 25%;
  • GPU minimum frequency around 400 MHz;
  • scx_lavd in Gaming mode.

Those settings make the machine feel responsive, but in this repeated burst-and-pause workload the complete performance profile had a real energy cost.

I would not claim that one setting alone caused the difference. The profiles change several things at once. To isolate that, I would need to benchmark the CPU and GPU floors, Wi-Fi power saving, and scheduler mode separately.

One wrong assumption about the scheduler

I also wanted the scheduler itself to follow the power profile.

Initially I assumed that scx_loader --auto meant “automatically select the scheduler mode based on the power profile”.

That was wrong.

In the version I was using, --auto refers to CPU utilization: it can launch scx_lavd after sustained high utilization. It has nothing to do with KDE’s power profiles.

CachyOS patches power-profiles-daemon to integrate with scx_loader, so the power profile can change the LAVD mode through its D-Bus API.

The final setup therefore looks like this:

ProfileLAVD mode
power-saverPowerSave
balancedAuto
performanceGaming

So the final setup gives each profile a consistent philosophy instead of changing only CPU frequency limits. It also gave me one useful reminder: don’t assume that a flag named --auto means what it sounds like. Looking at the actual binary and CachyOS documentation saved more time than debugging the wrong architecture.

So what is power-saver actually for?

After all these tests, I think the name “power-saver” is slightly misleading.

At least on this machine, it isn’t universally the profile that minimizes energy consumption.

Instead, its usefulness depends on the workload.

If the job is:

“Do this fixed amount of work as quickly as possible.”

then performance can actually be the most energy-efficient option.

If the job is:

“Keep the laptop running for several hours while occasionally doing small things.”

then power-saver makes much more sense.

The difference is whether time or work is the limiting factor.

ScenarioBest metricWinner
Fixed task: compile, encode, buildWh per taskperformance
Long session with intermittent workAverage Wpower-saver
Pure idleAverage Wbalanced in my measurements

That’s probably the most useful result I’ve got from this experiment.

Why keep the 60% CPU limit, then?

If 60% is actually bad for energy per task, why keep it?

Because battery life isn’t the only purpose of a power profile.

The lower ceiling also gives me:

  • lower peak power;
  • less heat;
  • less fan noise;
  • more predictable power consumption;
  • less thermal throttling during long sessions.

I keep the 60% limit not because it saves energy for every task — the encode test clearly says otherwise. I keep it because, when I sit in a cafe for a few hours, I care more about a quiet laptop with predictable heat and battery drain than about finishing a short compile ten seconds earlier.

That is what power-saver means on this laptop.

A few other things I found

There were a few smaller surprises along the way.

power-profiles-daemon and my watcher occasionally fight over min_perf_pct. The daemon reasserts a value of 9 after my handler writes its state.

It’s harmless because that value is already close to the effective minimum, but it was a good reminder that the configuration file isn’t necessarily the final state.

The second RAM stick also changes the baseline.

The machine originally reported UHD graphics; after adding a second memory module it became Iris Xe. The underlying silicon and i915 interface remain the same, so the GPU controls continue to work unchanged.

The extra memory does, however, add roughly 0.3–0.8 W of idle consumption.

So if I compare measurements from before and after the upgrade, that difference shouldn’t be mistaken for a regression in the power configuration.

Finally, the laptop only exposes s2idle rather than traditional deep sleep, and there is no disk-backed swap because the system uses zram.

That means suspend still consumes a noticeable amount of power overnight. There isn’t much software tuning that can change that without changing the sleep/hibernation setup itself.

What I’d change next

There are still a couple of experiments I’d like to run.

The most interesting one is the 60% CPU ceiling.

Maybe the optimal power-saver setting isn’t 60% at all.

I’d like to try 80% and find the point where the additional execution time stays small enough that the energy penalty disappears while the thermal benefits remain.

I’d also like to test a mixed GPU workload such as 1080p YouTube playback.

That should tell me whether the GPU ceiling and Wi-Fi power saving behave similarly when the machine is doing something closer to real desktop work.

For now, though, the main result is already clear.

There is no universally most energy-efficient performance profile.

If you care about the energy required to finish a task, running faster and going idle can win.

If you care about keeping a laptop alive through a long, intermittent work session, reducing peak power and allowing the machine to spend as much time as possible in low-power states can win.

And that is probably a more useful way to think about laptop power management than simply choosing between three buttons labelled “power saver”, “balanced” and “performance”.