mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: set the GPU to the lowest power state before applying a power limit if current consumption exceeds the new limit (#233)
* feat: attempt to clock down the GPU when setting a power limit lower than the current power consumption * add clippy settings
This commit is contained in:
parent
10f1e8c25a
commit
f5cd0dc25f
1
clippy.toml
Normal file
1
clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
too-many-lines-threshold = 150
|
@ -29,7 +29,12 @@ use std::{
|
|||||||
str::FromStr,
|
str::FromStr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tokio::{select, sync::Notify, task::JoinHandle, time::sleep};
|
use tokio::{
|
||||||
|
select,
|
||||||
|
sync::Notify,
|
||||||
|
task::JoinHandle,
|
||||||
|
time::{sleep, timeout},
|
||||||
|
};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
#[cfg(feature = "libdrm_amdgpu_sys")]
|
#[cfg(feature = "libdrm_amdgpu_sys")]
|
||||||
use {
|
use {
|
||||||
@ -40,6 +45,8 @@ use {
|
|||||||
|
|
||||||
type FanControlHandle = (Rc<Notify>, JoinHandle<()>);
|
type FanControlHandle = (Rc<Notify>, JoinHandle<()>);
|
||||||
|
|
||||||
|
const GPU_CLOCKDOWN_TIMEOUT_SECS: u64 = 3;
|
||||||
|
|
||||||
pub struct GpuController {
|
pub struct GpuController {
|
||||||
pub(super) handle: GpuHandle,
|
pub(super) handle: GpuHandle,
|
||||||
#[cfg(feature = "libdrm_amdgpu_sys")]
|
#[cfg(feature = "libdrm_amdgpu_sys")]
|
||||||
@ -493,7 +500,54 @@ impl GpuController {
|
|||||||
|
|
||||||
if let Some(cap) = config.power_cap {
|
if let Some(cap) = config.power_cap {
|
||||||
let hw_mon = self.first_hw_mon()?;
|
let hw_mon = self.first_hw_mon()?;
|
||||||
|
|
||||||
|
let current_usage = hw_mon
|
||||||
|
.get_power_input()
|
||||||
|
.or_else(|_| hw_mon.get_power_average())
|
||||||
|
.context("Could not get current power usage")?;
|
||||||
|
|
||||||
|
// When applying a power limit that's lower than the current power consumption,
|
||||||
|
// try to downclock the GPU first by forcing it into the lowest performance level.
|
||||||
|
// Workaround for behaviour described in https://github.com/ilya-zlobintsev/LACT/issues/207
|
||||||
|
let mut original_performance_level = None;
|
||||||
|
if current_usage > cap {
|
||||||
|
if let Ok(performance_level) = self.handle.get_power_force_performance_level() {
|
||||||
|
if self
|
||||||
|
.handle
|
||||||
|
.set_power_force_performance_level(PerformanceLevel::Low)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
debug!(
|
||||||
|
"waiting for the GPU to clock down before applying a new power limit"
|
||||||
|
);
|
||||||
|
|
||||||
|
match timeout(
|
||||||
|
Duration::from_secs(GPU_CLOCKDOWN_TIMEOUT_SECS),
|
||||||
|
wait_until_lowest_clock_level(&self.handle),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
debug!("GPU clocked down successfully");
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("GPU did not clock down after {GPU_CLOCKDOWN_TIMEOUT_SECS}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
original_performance_level = Some(performance_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hw_mon.set_power_cap(cap)?;
|
hw_mon.set_power_cap(cap)?;
|
||||||
|
|
||||||
|
// Reapply old power level
|
||||||
|
if let Some(level) = original_performance_level {
|
||||||
|
self.handle
|
||||||
|
.set_power_force_performance_level(level)
|
||||||
|
.context("Could not reapply original performance level")?;
|
||||||
|
}
|
||||||
} else if let Ok(hw_mon) = self.first_hw_mon() {
|
} else if let Ok(hw_mon) = self.first_hw_mon() {
|
||||||
if let Ok(default_cap) = hw_mon.get_power_cap_default() {
|
if let Ok(default_cap) = hw_mon.get_power_cap_default() {
|
||||||
hw_mon.set_power_cap(default_cap)?;
|
hw_mon.set_power_cap(default_cap)?;
|
||||||
@ -601,3 +655,21 @@ impl ClocksConfiguration {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn wait_until_lowest_clock_level(handle: &GpuHandle) {
|
||||||
|
loop {
|
||||||
|
match handle.get_core_clock_levels() {
|
||||||
|
Ok(levels) => {
|
||||||
|
if levels.active == Some(0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(250)).await;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("could not get core clock levels: {err}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user