Skip to main content

fan_curve/
fan_curve.rs

1use std::{error::Error, thread, time::Duration};
2
3use singe_nvml::{library::Library, types::TemperatureSensor};
4
5const GPU_INDEX: u32 = 0;
6const POLL_INTERVAL: Duration = Duration::from_secs(5);
7const POLLS: u32 = 1;
8const APPLY_CHANGES: bool = false;
9
10const CURVE: &[(u32, u32)] = &[(35, 25), (50, 40), (65, 60), (75, 80), (85, 100)];
11
12fn main() -> Result<(), Box<dyn Error>> {
13    let nvml = Library::create()?;
14    let device = nvml.device(GPU_INDEX)?;
15    let name = device.name()?;
16    let fans = device.num_fans()?;
17    let fan_limits = device.min_max_fan_speed()?;
18
19    println!("controlling GPU {GPU_INDEX}: {name}");
20    println!("fan speed range: {}%-{}%", fan_limits.min, fan_limits.max);
21    println!("curve: {CURVE:?}");
22    if !APPLY_CHANGES {
23        println!("dry run: set APPLY_CHANGES to true to write fan speeds");
24    }
25
26    for _ in 0..POLLS {
27        // Read the GPU temperature and choose a speed from the curve.
28        let temperature = device.temperature_reading(TemperatureSensor::Gpu)?;
29        let target_speed =
30            fan_speed_for_temperature(temperature).clamp(fan_limits.min, fan_limits.max);
31
32        // Apply the same target to every fan controller on the selected GPU.
33        for fan in 0..fans {
34            let current_speed = device.fan_speed(fan)?;
35            println!("fan {fan}: {temperature} C -> {target_speed}% (currently {current_speed}%)");
36
37            if APPLY_CHANGES && current_speed != target_speed {
38                device.set_fan_speed(fan, target_speed)?;
39            }
40        }
41
42        thread::sleep(POLL_INTERVAL);
43    }
44
45    Ok(())
46}
47
48fn fan_speed_for_temperature(temperature: u32) -> u32 {
49    let first = CURVE[0];
50    if temperature <= first.0 {
51        return first.1;
52    }
53
54    for window in CURVE.windows(2) {
55        let (low_temperature, low_speed) = window[0];
56        let (high_temperature, high_speed) = window[1];
57        if temperature <= high_temperature {
58            let span = high_temperature - low_temperature;
59            let offset = temperature - low_temperature;
60            let speed_delta = high_speed - low_speed;
61            return low_speed + speed_delta * offset / span;
62        }
63    }
64
65    CURVE[CURVE.len() - 1].1
66}