Skip to content

Commit 0b8551d

Browse files
authored
Merge pull request #26 from NotAShelf/auto-turbo
feature: dynamic CPU turbo management
2 parents 6ef0a60 + d687c03 commit 0b8551d

File tree

6 files changed

+335
-59
lines changed

6 files changed

+335
-59
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[package]
22
name = "superfreq"
3+
description = "Modern CPU frequency and power management utility for Linux"
34
version = "0.2.0"
45
edition = "2024"
6+
authors = ["NotAShelf <[email protected]>"]
7+
rust-version = "1.85"
58

69
[dependencies]
710
serde = { version = "1.0", features = ["derive"] }

README.md

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ Superfreq is a modern CPU frequency and power management utility for Linux
2020
systems. It provides intelligent control of CPU governors, frequencies, and
2121
power-saving features, helping optimize both performance and battery life.
2222

23-
It is greatly inspired by auto_cpufreq, but rewritten from ground up to provide
23+
It is greatly inspired by auto-cpufreq, but rewritten from ground up to provide
2424
a smoother experience with a more efficient and more correct codebase. Some
25-
features are omitted, and it is _not_ a drop-in replacement for auto_cpufreq,
25+
features are omitted, and it is _not_ a drop-in replacement for auto-cpufreq,
2626
but most common usecases are already implemented.
2727

2828
## Features
@@ -31,6 +31,8 @@ but most common usecases are already implemented.
3131
and turbo boost
3232
- **Intelligent Power Management**: Different profiles for AC and battery
3333
operation
34+
- **Dynamic Turbo Boost Control**: Automatically enables/disables turbo based on
35+
CPU load and temperature
3436
- **Fine-tuned Controls**: Adjust energy performance preferences, biases, and
3537
frequency limits
3638
- **Per-core Control**: Apply settings globally or to specific CPU cores
@@ -150,6 +152,15 @@ variable.
150152
governor = "performance"
151153
# Turbo boost setting: "always", "auto", or "never"
152154
turbo = "auto"
155+
# Enable or disable automatic turbo management (when turbo = "auto")
156+
enable_auto_turbo = true
157+
# Custom thresholds for auto turbo management
158+
turbo_auto_settings = {
159+
load_threshold_high = 70.0,
160+
load_threshold_low = 30.0,
161+
temp_threshold_high = 75.0,
162+
initial_turbo_state = false, # whether turbo should be initially enabled (false = disabled)
163+
}
153164
# Energy Performance Preference
154165
epp = "performance"
155166
# Energy Performance Bias (0-15 scale or named value)
@@ -166,6 +177,14 @@ max_freq_mhz = 3500
166177
[battery]
167178
governor = "powersave"
168179
turbo = "auto"
180+
# More conservative auto turbo settings on battery
181+
enable_auto_turbo = true
182+
turbo_auto_settings = {
183+
load_threshold_high = 80.0,
184+
load_threshold_low = 40.0,
185+
temp_threshold_high = 70.0,
186+
initial_turbo_state = false, # start with turbo disabled on battery for power savings
187+
}
169188
epp = "power"
170189
epb = "balance_power"
171190
platform_profile = "low-power"
@@ -209,6 +228,45 @@ Those are the more advanced features of Superfreq that some users might be more
209228
inclined to use than others. If you have a use-case that is not covered, please
210229
create an issue.
211230

231+
### Dynamic Turbo Boost Management
232+
233+
When using `turbo = "auto"` with `enable_auto_turbo = true`, Superfreq
234+
dynamically controls CPU turbo boost based on:
235+
236+
- **CPU Load Thresholds**: Enables turbo when load exceeds `load_threshold_high`
237+
(default 70%), disables when below `load_threshold_low` (default 30%)
238+
- **Temperature Protection**: Automatically disables turbo when CPU temperature
239+
exceeds `temp_threshold_high` (default 75°C)
240+
- **Hysteresis Control**: Prevents rapid toggling by maintaining previous state
241+
when load is between thresholds
242+
- **Configurable Initial State**: Sets the initial turbo state via
243+
`initial_turbo_state` (default: disabled) before system load data is available
244+
- **Profile-Specific Settings**: Configure different thresholds for battery vs.
245+
AC power
246+
247+
This feature optimizes performance and power consumption by providing maximum
248+
performance for demanding tasks while conserving energy during light workloads.
249+
250+
> [!TIP]
251+
> You can disable this logic with `enable_auto_turbo = false` to let the system
252+
> handle turbo boost natively when `turbo = "auto"`.
253+
254+
#### Turbo Boost Behavior Table
255+
256+
The table below explains how different combinations of `turbo` and
257+
`enable_auto_turbo` settings affect CPU turbo behavior:
258+
259+
| Setting | `enable_auto_turbo = true` | `enable_auto_turbo = false` |
260+
| ------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
261+
| `turbo = "always"` | **Always enabled**<br>Turbo is always active regardless of CPU load or temperature | **Always enabled**<br>Turbo is always active regardless of CPU load or temperature |
262+
| `turbo = "never"` | **Always disabled**<br>Turbo is always disabled regardless of CPU load or temperature | **Always disabled**<br>Turbo is always disabled regardless of CPU load or temperature |
263+
| `turbo = "auto"` | **Dynamically managed**<br>Superfreq enables/disables turbo based on CPU load and temperature thresholds | **System default**<br>Turbo is reset to system's default enabled state and is managed by the hardware/kernel |
264+
265+
> [!NOTE]
266+
> When `turbo = "auto"` and `enable_auto_turbo = false`, Superfreq ensures that
267+
> any previous turbo state restrictions are removed, allowing the
268+
> hardware/kernel to manage turbo behavior according to its default algorithms.
269+
212270
### Adaptive Polling
213271

214272
Superfreq includes a "sophisticated" (euphemism for complicated) adaptive
@@ -268,14 +326,16 @@ While reporting issues, please attach the results from `superfreq debug`.
268326
Contributions to Superfreq are always welcome! Whether it's bug reports, feature
269327
requests, or code contributions, please feel free to contribute.
270328

271-
If you are looking to reimplement features from auto_cpufreq, please consider
272-
opening an issue first and let us know what you have in mind. Certain features
273-
(such as the system tray) are deliberately ignored, and might not be desired in
274-
the codebase as they stand.
329+
> [!NOTE]
330+
> If you are looking to reimplement features from auto-cpufreq, please consider
331+
> opening an issue first and let us know what you have in mind. Certain features
332+
> (such as the system tray) are deliberately ignored, and might not be desired
333+
> in the codebase as they stand. Please discuss those features with us first :)
275334
276335
### Setup
277336

278-
You will need Cargo and Rust installed on your system. Rust 1.80 or later is required.
337+
You will need Cargo and Rust installed on your system. Rust 1.85 or later is
338+
required.
279339

280340
A `.envrc` is provided, and it's usage is encouraged for Nix users.
281341
Alternatively, you may use Nix for a reproducible developer environment
@@ -285,9 +345,9 @@ nix develop
285345
```
286346

287347
Non-Nix users may get the appropriate Cargo and Rust versions from their package
288-
manager.
348+
manager, or using something like Rustup.
289349

290-
### Formatting
350+
### Formatting & Lints
291351

292352
Please make sure to run _at least_ `cargo fmt` inside the repository to make
293353
sure all of your code is properly formatted. For Nix code, please use Alejandra.

src/config/types.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ pub struct ProfileConfig {
5050
pub min_freq_mhz: Option<u32>,
5151
pub max_freq_mhz: Option<u32>,
5252
pub platform_profile: Option<String>,
53-
pub turbo_auto_settings: Option<TurboAutoSettings>,
53+
#[serde(default)]
54+
pub turbo_auto_settings: TurboAutoSettings,
55+
#[serde(default)]
56+
pub enable_auto_turbo: bool,
5457
#[serde(skip_serializing_if = "Option::is_none")]
5558
pub battery_charge_thresholds: Option<BatteryChargeThresholds>,
5659
}
@@ -65,7 +68,8 @@ impl Default for ProfileConfig {
6568
min_freq_mhz: None, // no override
6669
max_freq_mhz: None, // no override
6770
platform_profile: None, // no override
68-
turbo_auto_settings: Some(TurboAutoSettings::default()),
71+
turbo_auto_settings: TurboAutoSettings::default(),
72+
enable_auto_turbo: default_enable_auto_turbo(),
6973
battery_charge_thresholds: None,
7074
}
7175
}
@@ -124,6 +128,9 @@ pub struct ProfileConfigToml {
124128
pub min_freq_mhz: Option<u32>,
125129
pub max_freq_mhz: Option<u32>,
126130
pub platform_profile: Option<String>,
131+
pub turbo_auto_settings: Option<TurboAutoSettings>,
132+
#[serde(default = "default_enable_auto_turbo")]
133+
pub enable_auto_turbo: bool,
127134
#[serde(skip_serializing_if = "Option::is_none")]
128135
pub battery_charge_thresholds: Option<BatteryChargeThresholds>,
129136
}
@@ -151,6 +158,8 @@ impl Default for ProfileConfigToml {
151158
min_freq_mhz: None,
152159
max_freq_mhz: None,
153160
platform_profile: None,
161+
turbo_auto_settings: None,
162+
enable_auto_turbo: default_enable_auto_turbo(),
154163
battery_charge_thresholds: None,
155164
}
156165
}
@@ -164,12 +173,18 @@ pub struct TurboAutoSettings {
164173
pub load_threshold_low: f32,
165174
#[serde(default = "default_temp_threshold_high")]
166175
pub temp_threshold_high: f32,
176+
/// Initial turbo boost state when no previous state exists.
177+
/// Set to `true` to start with turbo enabled, `false` to start with turbo disabled.
178+
/// This is only used at first launch or after a reset.
179+
#[serde(default = "default_initial_turbo_state")]
180+
pub initial_turbo_state: bool,
167181
}
168182

169183
// Default thresholds for Auto turbo mode
170184
pub const DEFAULT_LOAD_THRESHOLD_HIGH: f32 = 70.0; // enable turbo if load is above this
171185
pub const DEFAULT_LOAD_THRESHOLD_LOW: f32 = 30.0; // disable turbo if load is below this
172186
pub const DEFAULT_TEMP_THRESHOLD_HIGH: f32 = 75.0; // disable turbo if temperature is above this
187+
pub const DEFAULT_INITIAL_TURBO_STATE: bool = false; // by default, start with turbo disabled
173188

174189
const fn default_load_threshold_high() -> f32 {
175190
DEFAULT_LOAD_THRESHOLD_HIGH
@@ -180,13 +195,17 @@ const fn default_load_threshold_low() -> f32 {
180195
const fn default_temp_threshold_high() -> f32 {
181196
DEFAULT_TEMP_THRESHOLD_HIGH
182197
}
198+
const fn default_initial_turbo_state() -> bool {
199+
DEFAULT_INITIAL_TURBO_STATE
200+
}
183201

184202
impl Default for TurboAutoSettings {
185203
fn default() -> Self {
186204
Self {
187205
load_threshold_high: DEFAULT_LOAD_THRESHOLD_HIGH,
188206
load_threshold_low: DEFAULT_LOAD_THRESHOLD_LOW,
189207
temp_threshold_high: DEFAULT_TEMP_THRESHOLD_HIGH,
208+
initial_turbo_state: DEFAULT_INITIAL_TURBO_STATE,
190209
}
191210
}
192211
}
@@ -208,7 +227,8 @@ impl From<ProfileConfigToml> for ProfileConfig {
208227
min_freq_mhz: toml_config.min_freq_mhz,
209228
max_freq_mhz: toml_config.max_freq_mhz,
210229
platform_profile: toml_config.platform_profile,
211-
turbo_auto_settings: Some(TurboAutoSettings::default()),
230+
turbo_auto_settings: toml_config.turbo_auto_settings.unwrap_or_default(),
231+
enable_auto_turbo: toml_config.enable_auto_turbo,
212232
battery_charge_thresholds: toml_config.battery_charge_thresholds,
213233
}
214234
}
@@ -282,6 +302,10 @@ const fn default_stats_file_path() -> Option<String> {
282302
None
283303
}
284304

305+
const fn default_enable_auto_turbo() -> bool {
306+
true
307+
}
308+
285309
#[derive(Deserialize, Serialize, Debug, Clone)]
286310
pub struct DaemonConfigToml {
287311
#[serde(default = "default_poll_interval_sec")]

src/cpu.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::core::{GovernorOverrideMode, TurboSetting};
22
use crate::util::error::ControlError;
33
use core::str;
4+
use log::debug;
45
use std::{fs, io, path::Path, string::ToString};
56

67
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
@@ -216,12 +217,19 @@ pub fn set_turbo(setting: TurboSetting) -> Result<()> {
216217
let value_pstate = match setting {
217218
TurboSetting::Always => "0", // no_turbo = 0 means turbo is enabled
218219
TurboSetting::Never => "1", // no_turbo = 1 means turbo is disabled
219-
TurboSetting::Auto => return Err(ControlError::InvalidValueError("Turbo Auto cannot be directly set via intel_pstate/no_turbo or cpufreq/boost. System default.".to_string())),
220+
// Auto mode is handled at the engine level, not directly at the sysfs level
221+
TurboSetting::Auto => {
222+
debug!("Turbo Auto mode is managed by engine logic based on system conditions");
223+
return Ok(());
224+
}
220225
};
221226
let value_boost = match setting {
222227
TurboSetting::Always => "1", // boost = 1 means turbo is enabled
223228
TurboSetting::Never => "0", // boost = 0 means turbo is disabled
224-
TurboSetting::Auto => return Err(ControlError::InvalidValueError("Turbo Auto cannot be directly set via intel_pstate/no_turbo or cpufreq/boost. System default.".to_string())),
229+
TurboSetting::Auto => {
230+
debug!("Turbo Auto mode is managed by engine logic based on system conditions");
231+
return Ok(());
232+
}
225233
};
226234

227235
// AMD specific paths

0 commit comments

Comments
 (0)