Allow for overriding the CPU turbo setting via CLI and GUI. (#859)

* Allow for overriding the CPU turbo setting via CLI and GUI.

* Add Turbo mode override info to README

---------

Co-authored-by: JuanR4140 <N/A>
This commit is contained in:
JuanR4140 2025-06-29 23:54:55 -05:00 committed by GitHub
parent fabeee6ed7
commit 36aca759c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 107 additions and 6 deletions

View File

@ -47,13 +47,15 @@ Example of `auto-cpufreq --stats` CLI output
- [Configuring auto-cpufreq](#configuring-auto-cpufreq)
- [1: power_helper.py script (Snap package install only)](#1-power_helperpy-script-snap-package-install-only)
- [2: `--force` governor override](#2---force-governor-override)
- [3: auto-cpufreq config file](#3-auto-cpufreq-config-file)
- [3: `--turbo` mode override](#3---turbo-mode-override)
- [4: auto-cpufreq config file](#4-auto-cpufreq-config-file)
- [Example config file contents](#example-config-file-contents)
- [How to run auto-cpufreq](#how-to-run-auto-cpufreq)
- [auto-cpufreq modes and options](#auto-cpufreq-modes-and-options)
- [monitor](#monitor)
- [live](#live)
- [overriding governor](#overriding-governor)
- [overriding turbo mode](#overriding-turbo-mode)
- [Install - auto-cpufreq daemon](#install---auto-cpufreq-daemon)
- [Update - auto-cpufreq update](#update---auto-cpufreq-update)
- [Remove - auto-cpufreq daemon](#remove---auto-cpufreq-daemon)
@ -319,7 +321,15 @@ However, you can override this behaviour by switching to `performance` or `power
See [`--force` flag](#overriding-governor) for more info.
### 3: auto-cpufreq config file
### 3: `--turbo` mode override
By default, auto-cpufreq handles CPU turbo mode automatically, enabling it under load and disabling it otherwise to balance performance and efficiency.
However, you can override this behavior by forcing CPU turbo's mode to `always` or `never`. Setting to `always` keeps turbo mode always enabled, allowing the CPU to reach its maximum frequency at the cost of higher energy use (battery consumption). `never`, on the other hand, keeps turbo mode always disabled, limiting the CPU's maximum frequency to extend battery life.
See [`--turbo` flag](#overriding-turbo-mode) for more info.
### 4: auto-cpufreq config file
You can configure separate profiles for the battery and power supply. These profiles will let you pick which governor to use, as well as how and when turbo boost is enabled. The possible values for turbo boost behavior are `always`, `auto`, and `never`. The default behavior is `auto`, which only activates turbo during high load.
@ -451,6 +461,9 @@ auto-cpufreq should be run with with one of the following options:
- [force=TEXT](#overriding-governor)
- Force use of either the "powersave" or "performance" governor, or set to "reset" to go back to normal mode
- [turbo=TEXT](#overriding-turbo-mode)
- Force use of CPU turbo mode, if supported, with "never" or "always", or set to "auto" to automatically handle turbo mode
- config=TEXT
- Use config file at designated path
@ -492,6 +505,13 @@ Necessary changes are temporarily made to the system over time, but this process
Force use of either the "powersave" or "performance" governor, or set to "reset" to go back to normal mode.
Please note that any set override will persist even after reboot.
### Overriding Turbo mode
`sudo auto-cpufreq --turbo=mode`
Force use of CPU turbo mode, if supported, with "never" or "always", or set to "auto" to automatically handle turbo mode.
Please note that any set override will persist even after reboot.
### Install - auto-cpufreq daemon
Necessary changes are made to the system over time and this process will continue across reboots. The daemon is deployed and started as a systemd service. Changes are made automatically and live stats are generated for monitoring purposes.

View File

@ -26,6 +26,7 @@ from threading import Thread
@click.option("--update", is_flag=False, help="Update daemon and package for (permanent) automatic CPU optimizations", flag_value="--update")
@click.option("--remove", is_flag=True, help="Remove daemon for (permanent) automatic CPU optimizations")
@click.option("--force", is_flag=False, help="Force use of either \"powersave\" or \"performance\" governors. Setting to \"reset\" will go back to normal mode")
@click.option("--turbo", is_flag=False, help="Force use of CPU turbo mode, if supported, with \"never\" or \"always\". Setting to \"auto\" automatically handles turbo mode")
@click.option("--config", is_flag=False, required=False, help="Use config file at defined path",)
@click.option("--stats", is_flag=True, help="View live stats of CPU optimizations made by daemon")
@click.option("--get-state", is_flag=True, hidden=True)
@ -34,7 +35,7 @@ from threading import Thread
@click.option("--debug", is_flag=True, help="Show debug info (include when submitting bugs)")
@click.option("--version", is_flag=True, help="Show currently installed version")
@click.option("--donate", is_flag=True, help="Support the project")
def main(monitor, live, daemon, install, update, remove, force, config, stats, get_state,
def main(monitor, live, daemon, install, update, remove, force, turbo, config, stats, get_state,
bluetooth_boot_off, bluetooth_boot_on, debug, version, donate):
# display info if config file is used
config_path = find_config_file(config)
@ -58,6 +59,11 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
not_running_daemon_check()
root_check() # Calling root_check before set_override as it will require sudo access
set_override(force) # Calling set override, only if force has some values
if turbo is not None:
not_running_daemon_check()
root_check()
set_turbo_override(turbo)
if monitor:
root_check()

View File

@ -47,13 +47,15 @@ powersave_load_threshold = (75 * CPUS) / 100
auto_cpufreq_stats_file = None
auto_cpufreq_stats_path = None
# track governor override
# track governor override and turbo boost override
if IS_INSTALLED_WITH_SNAP:
auto_cpufreq_stats_path = Path("/var/snap/auto-cpufreq/current/auto-cpufreq.stats")
governor_override_state = Path("/var/snap/auto-cpufreq/current/override.pickle")
turbo_override_state = Path("/var/snap/auto-cpufreq/current/turbo-override.pickle")
else:
auto_cpufreq_stats_path = Path("/var/run/auto-cpufreq.stats")
governor_override_state = Path("/opt/auto-cpufreq/override.pickle")
turbo_override_state = Path("/opt/auto-cpufreq/turbo-override.pickle")
def file_stats():
global auto_cpufreq_stats_file
@ -76,6 +78,22 @@ def set_override(override):
print("Governor override removed")
elif override is not None: print("Invalid option.\nUse force=performance, force=powersave, or force=reset")
def get_turbo_override():
if os.path.isfile(turbo_override_state):
with open(turbo_override_state, "rb") as store: return load(store)
else: return "auto"
def set_turbo_override(override):
if override in ["never", "always"]:
with open(turbo_override_state, "wb") as store:
dump(override, store)
print(f"Set turbo boost override to {override}")
elif override == "auto":
if os.path.isfile(turbo_override_state):
os.remove(turbo_override_state)
print("Turbo override removed")
elif override is not None: print("Invalid option.\nUse turbo=always, turbo=never, or turbo=auto")
# get distro name
try: dist_name = distro.id()
except PermissionError:
@ -202,6 +220,14 @@ def turbo(value: bool = None):
else:
print("Warning: CPU turbo is not available")
return False
turbo_override = get_turbo_override()
if turbo_override != "auto":
# Set the value in respect to if turbo override is enabled or not.
if turbo_override == "always":
value = True
elif turbo_override == "never":
value = False
if value is not None:
try: f.write_text(f"{int(value ^ inverse)}\n")
@ -559,6 +585,7 @@ def set_powersave():
cpuload, load1m= get_load()
auto = conf["battery"]["turbo"] if conf.has_option("battery", "turbo") else "auto"
auto = get_turbo_override() if (get_turbo_override() != "auto") else auto # Override turbo if override file is present, otherwise stick to config.
if auto == "always":
print("Configuration file enforces turbo boost")
@ -670,6 +697,7 @@ def set_performance():
cpuload, load1m = get_load()
auto = conf["charger"]["turbo"] if conf.has_option("charger", "turbo") else "auto"
auto = get_turbo_override() if (get_turbo_override() != "auto") else auto # Override turbo if override file is present, otherwise stick to config.
if auto == "always":
print("Configuration file enforces turbo boost")

View File

@ -9,7 +9,8 @@ from threading import Thread
from auto_cpufreq.core import check_for_update, is_running
from auto_cpufreq.globals import GITHUB, IS_INSTALLED_WITH_SNAP
from auto_cpufreq.gui.objects import CPUFreqStatsLabel, CurrentGovernorBox, DaemonNotRunningView, DropDownMenu, RadioButtonView, SystemStatsLabel, UpdateDialog
from auto_cpufreq.gui.objects import CPUFreqStatsLabel, CurrentGovernorBox, DaemonNotRunningView, DropDownMenu, RadioButtonView, CPUTurboOverride, SystemStatsLabel, UpdateDialog
from auto_cpufreq.gui.objects import get_stats
if IS_INSTALLED_WITH_SNAP:
CSS_FILE = "/snap/auto-cpufreq/current/style.css"
@ -48,6 +49,8 @@ class ToolWindow(Gtk.Window):
self.currentgovernor = CurrentGovernorBox()
self.vbox_right.pack_start(self.currentgovernor, False, False, 0)
self.vbox_right.pack_start(RadioButtonView(), False, False, 0)
if "Warning: CPU turbo is not available" not in get_stats():
self.vbox_right.pack_start(CPUTurboOverride(), False, False, 0)
self.cpufreqstats = CPUFreqStatsLabel()
self.vbox_right.pack_start(self.cpufreqstats, False, False, 0)

View File

@ -9,7 +9,7 @@ from os.path import isfile
from platform import python_version
from subprocess import getoutput, PIPE, run
from auto_cpufreq.core import distro_info, get_formatted_version, get_override, sysinfo
from auto_cpufreq.core import distro_info, get_formatted_version, get_override, get_turbo_override, sysinfo
from auto_cpufreq.globals import GITHUB, IS_INSTALLED_WITH_AUR, IS_INSTALLED_WITH_SNAP
PKEXEC_ERROR = "Error executing command as another user: Not authorized\n\nThis incident has been reported.\n"
@ -78,6 +78,50 @@ class RadioButtonView(Gtk.Box):
if self.set_by_app: self.set_by_app = False
self.default.set_active(True)
class CPUTurboOverride(Gtk.Box):
def __init__(self):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.set_hexpand(True)
self.hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.label = Gtk.Label("CPU Turbo Override", name="bold")
self.auto = Gtk.RadioButton.new_with_label_from_widget(None, "Auto")
self.auto.connect("toggled", self.on_button_toggled, "auto")
self.auto.set_halign(Gtk.Align.END)
self.never = Gtk.RadioButton.new_with_label_from_widget(self.auto, "Never")
self.never.connect("toggled", self.on_button_toggled, "never")
self.never.set_halign(Gtk.Align.END)
self.always = Gtk.RadioButton.new_with_label_from_widget(self.auto, "Always")
self.always.connect("toggled", self.on_button_toggled, "always")
self.always.set_halign(Gtk.Align.END)
self.set_by_app = True
self.set_selected()
self.pack_start(self.label, False, False, 0)
self.pack_start(self.auto, True, True, 0)
self.pack_start(self.never, True, True, 0)
self.pack_start(self.always, True, True, 0)
def on_button_toggled(self, button, override):
if button.get_active():
if not self.set_by_app:
result = run(f"pkexec auto-cpufreq --turbo={override}", shell=True, stdout=PIPE, stderr=PIPE)
if result.stderr.decode() == PKEXEC_ERROR: self.set_selected()
else: self.set_by_app = False
def set_selected(self):
override = get_turbo_override()
match override:
case "never": self.never.set_active(True)
case "always": self.always.set_active(True)
case "auto":
# because this is the default button, it does not trigger the callback when set by the app
if self.set_by_app: self.set_by_app = False
self.auto.set_active(True)
class CurrentGovernorBox(Gtk.Box):
def __init__(self):
super().__init__(spacing=25)