I wanted my desktop to run cooler during normal work without permanently crippling it for gaming.

My first assumption was simple: the CPU had to be the problem. A Ryzen 9 3950X is a 105 W TDP, 16-core part that runs warm even at idle — it seemed like the obvious place to start.

That assumption was not completely wrong, but it was wrong enough to waste time.

What I tried first: CPU tuning

I installed power-profiles-daemon1 and checked what was available:

powerprofilesctl list
powerprofilesctl get

On my system, the available profiles were performance, balanced, and power-saver. The active profile was initially balanced, and my CPU driver was amd-pstate-epp.2

Next I started reducing the number of active threads. My Ryzen 9 3950X has 16 physical cores and 32 logical threads (SMT). Linux exposes per-CPU hotplug control through sysfs,3 so I could offline CPUs at runtime:

# Disable SMT siblings (16C/16T)
for i in $(seq 16 31); do
  echo 0 | sudo tee /sys/devices/system/cpu/cpu$i/online
done

# Go further: 8C/8T
for i in $(seq 8 31); do
  echo 0 | sudo tee /sys/devices/system/cpu/cpu$i/online
done

Switching to power-saver and reducing to 8 threads helped, but less than I expected. The CPU was already parking idle cores aggressively on its own.2

What actually mattered: the GPU

The bigger finding came from looking at the GPU:

nvidia-smi --query-gpu=temperature.gpu,power.draw,clocks.gr,clocks.mem \
  --format=csv -l 1

At my normal desktop setting of 5120x1440 @ 240 Hz, the RTX 3080 kept its memory clock at 810 MHz even when completely idle. At 60 Hz, the memory clock dropped to 405 MHz.

That alone roughly halved the GPU’s idle power draw.

Why 240 Hz costs more at idle

High refresh rates force the GPU’s memory controller to work harder. At 5120x1440 with 10-bit color, each frame is about 29 MB. At 240 Hz, that is roughly 7 GB/s of memory bandwidth just for framebuffer scanout — even if nothing on screen changes. At 60 Hz, that drops to about 1.7 GB/s.4

NVIDIA GPUs respond to this bandwidth demand by locking the memory clock to a higher P-state.5 The higher clock means more power, more heat, and more fan activity — all for rendering a static desktop.

The two modes

Based on all of this, I ended up with two profiles I can switch between:

Office mode

  • Power profile: power-saver
  • CPUs online: 0–7 (8C/8T)
  • Monitor: 5120x1440 @ 60 Hz

Gaming mode

  • Power profile: balanced
  • CPUs online: 0–31 (16C/32T)
  • Monitor: 5120x1440 @ 240 Hz

Measured results

To verify this was not just a feeling, I ran a structured benchmark: 30 minutes of sampling at 2-second intervals in each mode, with a 120-second settle period after switching. Background load was kept constant across both tests (Chrome with tabs, YouTube, Steam, terminal emulators). The full methodology is described at the end of this post.

Results (792 samples per mode, 30 minutes each)

MetricGamingOfficeDelta
CPU Tctlavg 45.5°C (44.9–47.9)avg 43.6°C (43.1–45.8)-1.9°C
CPU currentavg 11.3 A (6–24)avg 6.3 A (5–15)-44%
GPU temperatureavg 48.8°C (43–55)avg 41.4°C (40–43)-7.4°C
GPU power drawavg 35.4 W (17–142)avg 17.5 W (17–64)-51%
GPU memory clock810 MHz (94% of samples)405 MHz (99.9% of samples)-50%

What the numbers mean

The GPU is the dominant factor. During gaming mode, the 240 Hz refresh rate holds the GPU memory clock at 810 MHz, which forces a steady-state power draw of about 36 W. Switching to 60 Hz drops the memory clock to 405 MHz and power draw to about 17.5 W. That is an 18 W continuous reduction from a single setting change.

The CPU side is more modest. Reducing from 32 threads to 8 and switching to power-saver cut the average current draw by 44% (from 11.3 A to 6.3 A), but the temperature only moved 1.9°C. Modern CPUs aggressively park idle cores on their own,2 so offlining them at idle has a smaller thermal effect than you might expect. The bigger impact is on current draw, which suggests less voltage regulator activity and fewer short boost bursts on background tasks.

The GPU temperature trend over 30 minutes also shows the system was still approaching steady state:

  • Gaming GPU temp: 46.3°C (first 5 min) → 48.9°C (last 5 min), still rising
  • Office GPU temp: 42.8°C (first 5 min) → 40.1°C (last 5 min), still falling

A longer test would likely show a slightly larger temperature gap at true steady state.

Transient spikes

Both modes showed occasional transient GPU spikes from background activity (compositor, browser rendering). In gaming mode, GPU power briefly hit 142 W with memory clocks at 9501 MHz. In office mode, the worst spike was 64 W at 5001 MHz. These are not representative of idle behavior and lasted only a few seconds each.

Desktop launchers

I did not want to type commands every time, so I created two small scripts and added them as clickable desktop icons.

The scripts use kscreen-doctor to switch the display mode. The mode numbers are specific to your monitor — find yours with:6

kscreen-doctor -o
# Mode 2: 5120x1440@239.76
# Mode 3: 5120x1440@59.98

Office mode script

#!/usr/bin/env bash
set -e

pkexec bash -lc '
powerprofilesctl set power-saver
for i in $(seq 8 31); do
  echo 0 > /sys/devices/system/cpu/cpu$i/online
done
'

kscreen-doctor output.DP-2.mode.3

Gaming mode script

#!/usr/bin/env bash
set -e

pkexec bash -lc '
powerprofilesctl set balanced
for i in $(seq 1 31); do
  echo 1 > /sys/devices/system/cpu/cpu$i/online
done
'

kscreen-doctor output.DP-2.mode.2

A small but important detail: the privileged part (pkexec) has to run first and the display mode change second. Otherwise the monitor briefly goes black before the authentication prompt appears, which felt wrong.

Setting up the desktop icons

I saved both scripts to ~/.local/bin/ and made them executable:

chmod +x ~/.local/bin/office-mode.sh
chmod +x ~/.local/bin/gaming-mode.sh

Then I created two .desktop files on ~/Desktop/:

# Office Mode.desktop
[Desktop Entry]
Type=Application
Name=Office Mode
Exec=/home/bryan/.local/bin/office-mode.sh
Icon=preferences-system-power-management
Terminal=false
# Gaming Mode.desktop
[Desktop Entry]
Type=Application
Name=Gaming Mode
Exec=/home/bryan/.local/bin/gaming-mode.sh
Icon=applications-games
Terminal=false

KDE picks these up as clickable desktop icons. One click, pkexec asks for the password, and the mode switches.

Important notes

1. This is temporary unless you automate it

Offlining CPUs through /sys/devices/system/cpu/cpu*/online does not survive a reboot.3 That is actually nice for testing because it is easy to undo.

2. GUI frequency monitors can be misleading

Some desktop tools still display stale or confusing per thread frequencies after CPUs are offlined.

The real source of truth is:

cat /sys/devices/system/cpu/online

3. Not every warm sensor reading matters

I also saw jc42 sensors showing ALARM (HIGH, CRIT) with thresholds set to 0.0°C. That is not a real overheating event. It is just a broken or meaningless threshold configuration for that sensor.

4. Idle temperatures will always move around

Do not obsess over every single degree. Idle temperatures fluctuate. What matters is the overall trend and whether the system behaves more sensibly over time.

Conclusion

I started by blaming the CPU. That was the right instinct in the wrong proportions.

The measured data showed that the GPU was the dominant factor: switching from 240 Hz to 60 Hz at idle cut GPU power draw by 51% (from 35.4 W to 17.5 W average) and reduced GPU temperature by 7.4°C. The CPU changes helped current draw significantly (-44%) but only moved temperature by 1.9°C.

The final answer was not one magic setting. It was combining several smaller changes into a sane office mode:

  • power-saver
  • 8C / 8T
  • 60 Hz

If you have a high refresh ultrawide and an NVIDIA card, check your GPU memory clocks before you start blaming the CPU alone.


How I measured this

The numbers in this post come from a bash script that automates the entire A/B comparison. Here is how it works:

  1. The script runs as root (sudo -E) so it can write to /sys/devices/system/cpu/cpuN/online and call powerprofilesctl. The -E flag preserves the display environment so the script can also switch the monitor refresh rate via kscreen-doctor.
  2. It inhibits sleep and screensaver for the entire run using systemd-inhibit.
  3. It switches to gaming mode (sets balanced profile, brings all CPUs online, switches to 240 Hz), then waits 120 seconds for thermals to settle.
  4. For 30 minutes, it samples every 2 seconds: CPU Tctl from sensors (k10temp), CPU current from the motherboard VRM sensor, and GPU temperature, power draw, graphics clock, and memory clock from nvidia-smi.
  5. It switches to office mode (sets power-saver, offlines CPUs 8–31, switches to 60 Hz), waits another 120 seconds, and repeats the same 30-minute sampling.
  6. It restores the system to balanced mode and prints a min/max/avg comparison table.

All samples are written to timestamped CSV files. The sensor names, display output identifier, and kscreen-doctor mode numbers are hardware-specific — if you want to reproduce this on your own system, you will need to adjust those to match your setup. Run sensors, nvidia-smi -L, and kscreen-doctor -o to find the right values for your machine.

Test environment

CPUAMD Ryzen 9 3950X (16C/32T)
GPUNVIDIA GeForce RTX 3080 (driver 595.58.03)
RAM32 GB
OSCachyOS, kernel 6.19.11-1-cachyos
DesktopKDE Plasma on Wayland
Samples792 per mode, 30 minutes at 2-second intervals
Date2026-04-12

  1. power-profiles-daemon is a D-Bus daemon that exposes system-wide power profiles (performance, balanced, power-saver) to desktop environments. It integrates with CPU scaling drivers including amd_pstate. Source: https://gitlab.freedesktop.org/upower/power-profiles-daemon ↩︎

  2. The amd-pstate-epp driver uses AMD’s Collaborative Processor Performance Control (CPPC) to let the hardware manage frequency scaling, with the OS providing energy performance preference hints. In power-saver mode, the EPP hint is set to prefer efficiency. The driver already implements aggressive idle management at the hardware level. Source: https://docs.kernel.org/admin-guide/pm/amd-pstate.html ↩︎ ↩︎ ↩︎

  3. Linux CPU hotplug allows individual logical CPUs to be taken offline at runtime by writing 0 to /sys/devices/system/cpu/cpuN/online. CPU 0 cannot be offlined on most architectures. Changes do not persist across reboots. Source: https://docs.kernel.org/core-api/cpu_hotplug.html ↩︎ ↩︎

  4. Framebuffer bandwidth estimate: 5120 x 1440 pixels x 4 bytes per pixel (10-bit per channel, packed as 32-bit) = 29.5 MB per frame. At 240 Hz: 29.5 MB x 240 = 7.08 GB/s. At 60 Hz: 29.5 MB x 60 = 1.77 GB/s. This is the raw scanout bandwidth; actual memory traffic includes cursor planes, overlays, and compositor buffers. ↩︎

  5. NVIDIA GPUs use dynamic power states (P-states) for memory clocks. Higher display bandwidth requirements force higher memory P-states. The specific clock values (810 MHz, 405 MHz) were observed on this RTX 3080 via nvidia-smi --query-gpu=clocks.mem --format=csv and may differ on other cards or driver versions. ↩︎

  6. kscreen-doctor is a CLI tool for KDE’s display management layer (libkscreen). Mode indices are specific to the connected display and may change if the monitor or cable changes. Run kscreen-doctor -o to see available modes. Source: https://invent.kde.org/plasma/libkscreen ↩︎