feat(core): inference_thread_count halves on battery
Adds battery-state awareness to the thread-count helper: when probe_power_state() returns OnBattery, chosen is halved before the [MIN, MAX] clamp. Also removes the leading underscore from the workload/gpu_offloaded parameters (they are silenced via let _ = ... until the GPU-clamp task). New test battery_halves_thread_count verifies on_battery <= on_ac and >= MIN when the host has more than MIN physical cores. Restructured to sequential with_override calls (not nested) to avoid re-entrant deadlock on power::TEST_LOCK. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,7 +40,7 @@ const GPU_FLOOR_WHISPER: usize = 4;
|
|||||||
/// 3. on battery → base /= 2.
|
/// 3. on battery → base /= 2.
|
||||||
/// 4. gpu_offloaded → base = min(base, gpu_floor(workload)).
|
/// 4. gpu_offloaded → base = min(base, gpu_floor(workload)).
|
||||||
/// 5. clamp to [MIN_INFERENCE_THREADS, MAX_INFERENCE_THREADS].
|
/// 5. clamp to [MIN_INFERENCE_THREADS, MAX_INFERENCE_THREADS].
|
||||||
pub fn inference_thread_count(_workload: Workload, _gpu_offloaded: bool) -> usize {
|
pub fn inference_thread_count(workload: Workload, gpu_offloaded: bool) -> usize {
|
||||||
if let Ok(s) = std::env::var("MAGNOTIA_INFERENCE_THREADS") {
|
if let Ok(s) = std::env::var("MAGNOTIA_INFERENCE_THREADS") {
|
||||||
if let Ok(n) = s.parse::<usize>() {
|
if let Ok(n) = s.parse::<usize>() {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -49,13 +49,17 @@ pub fn inference_thread_count(_workload: Workload, _gpu_offloaded: bool) -> usiz
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let physical = num_cpus::get_physical();
|
let physical = num_cpus::get_physical();
|
||||||
let chosen = if physical > 0 {
|
let mut chosen = if physical > 0 {
|
||||||
physical
|
physical
|
||||||
} else {
|
} else {
|
||||||
std::thread::available_parallelism()
|
std::thread::available_parallelism()
|
||||||
.map(|p| p.get())
|
.map(|p| p.get())
|
||||||
.unwrap_or(MIN_INFERENCE_THREADS)
|
.unwrap_or(MIN_INFERENCE_THREADS)
|
||||||
};
|
};
|
||||||
|
if power::probe_power_state() == PowerState::OnBattery {
|
||||||
|
chosen /= 2;
|
||||||
|
}
|
||||||
|
let _ = (workload, gpu_offloaded); // GPU clamp added in next task
|
||||||
chosen.clamp(MIN_INFERENCE_THREADS, MAX_INFERENCE_THREADS)
|
chosen.clamp(MIN_INFERENCE_THREADS, MAX_INFERENCE_THREADS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,4 +105,27 @@ mod tests {
|
|||||||
fn workload_variants_distinct() {
|
fn workload_variants_distinct() {
|
||||||
assert_ne!(Workload::Llm, Workload::Whisper);
|
assert_ne!(Workload::Llm, Workload::Whisper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn battery_halves_thread_count() {
|
||||||
|
with_thread_env_lock(|| {
|
||||||
|
std::env::remove_var("MAGNOTIA_INFERENCE_THREADS");
|
||||||
|
// Measure on battery, then on AC — sequential, not nested,
|
||||||
|
// to avoid re-entrant deadlock on power::TEST_LOCK.
|
||||||
|
let on_battery = crate::power::with_override(Some(PowerState::OnBattery), || {
|
||||||
|
inference_thread_count(Workload::Llm, false)
|
||||||
|
});
|
||||||
|
let on_ac = crate::power::with_override(Some(PowerState::OnAc), || {
|
||||||
|
inference_thread_count(Workload::Llm, false)
|
||||||
|
});
|
||||||
|
// on_battery should be roughly half of on_ac, clamped to MIN.
|
||||||
|
if on_ac > MIN_INFERENCE_THREADS {
|
||||||
|
assert!(
|
||||||
|
on_battery <= on_ac,
|
||||||
|
"battery ({on_battery}) should be <= AC ({on_ac})"
|
||||||
|
);
|
||||||
|
assert!(on_battery >= MIN_INFERENCE_THREADS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user