Posted on 5 mins read

Managing power consumption on Linux laptops has always been a balancing act between performance and battery life. Today, I'm excited to share Observer, a new open-source tool that intelligently manages CPU cores based on your system's power state and load.

The Problem

Modern laptops come with increasingly powerful CPUs, often featuring multiple cores and HyperThreading. While this provides excellent performance when needed, having all cores active can significantly impact battery life during lighter workloads.

Traditional power management tools often focus on CPU frequency scaling, but they don't always handle core management optimally. This is where Observer comes in.

How Observer Works

Observer takes a different approach by dynamically managing which CPU cores are active. Here's how it works:

  1. Power State Detection: Observer continuously monitors whether your laptop is running on battery or AC power.

  2. Smart Core Management:

    • On battery power, it reduces the number of active cores to a configurable percentage (default 50%)
    • When plugged in, all cores are available for maximum performance
    • Cores are enabled/disabled in pairs to maintain proper HyperThreading relationships
  3. Load-Based Scaling:

    • Monitors real-time CPU load across active cores
    • Uses windowed load averaging for stable decisions
    • Implements hysteresis to prevent rapid core switching
    • Maintains a minimum number of cores for system responsiveness
  4. Smooth Transitions:

    • Gracefully handles core state changes with configurable delays
    • Ensures system stability during transitions
    • Properly manages core pairs for HyperThreading
    • Restores all cores on shutdown

Real-World Impact

Let's look at a practical example. On my laptop with an 8-core/16-thread CPU:

  • On AC Power: All 16 threads available for maximum performance
  • On Battery (Light Load): Runs with 4 cores (8 threads), significantly reducing power consumption
  • On Battery (High Load): Dynamically scales up to use more cores as needed
  • Battery Life Impact: Early testing shows 10-20% improvement in battery life during light workloads

Technical Implementation

Observer is written in Rust, chosen for its safety guarantees and performance. Here's a look at the core management logic:

impl CPUTopology {
    pub fn get_cores_to_enable(&self, target_count: usize) -> Vec<usize> {
        let mut cores = Vec::new();

        // Always enable CPU 0 and its sibling if exists
        cores.push(0);
        if let Some((_, sibling)) = self.physical_cores.iter().find(|(core, _)| *core == 0) {
            cores.push(*sibling);
        }

        // Enable additional cores in pairs
        for &(core, sibling) in self.physical_cores.iter().skip(1) {
            if cores.len() >= target_count {
                break;
            }
            cores.push(core);
            cores.push(sibling);
        }

        cores.sort();
        cores
    }
}

The project follows modern Rust best practices:

  • Load tracking with configurable window averaging
  • Hysteresis-based core scaling decisions
  • Proper HyperThreading pair management
  • Graceful cleanup on shutdown
  • Comprehensive error handling and logging
  • Configuration using TOML
  • Systemd integration

Getting Started

Installation is straightforward. On Arch Linux:

yay -S observer

Or using the quick install script:

curl -sL "https://github.com/voioo/observer/releases/latest/download/observer-linux-amd64.tar.gz" | \
  sudo bash -c 'tar xz -C /tmp && bash /tmp/install.sh'

Configure through /etc/observer/config.toml:

# Percentage of cores to enable on battery
battery_core_percentage = 50

# Delay in milliseconds between enabling/disabling each core
transition_delay_ms = 500

# How often to check power state and CPU load (in seconds)
check_interval_sec = 5

# CPU load threshold percentage to trigger core count adjustment
cpu_load_threshold = 75.0

# Minimum cores to keep enabled
min_cores = 2

# Minimum time between core count changes (in seconds)
min_change_interval_sec = 10

# Window size for load averaging (in seconds)
load_window_sec = 30

Future Plans

Observer is actively developed, with several exciting features planned:

  1. Temperature-Based Management: Adjusting core count based on CPU temperature
  2. Workload Profiles: Different configurations for different types of workloads
  3. GPU Integration: Coordinating with GPU power management
  4. Power Consumption Metrics: Built-in power usage monitoring
  5. Machine Learning Integration: Using historical data to predict optimal core counts

Contributing

Observer is open source and welcomes contributions! Whether it's code, documentation, or bug reports, every contribution helps. Check out my GitHub repository to get started.

Conclusion

Observer demonstrates how modern systems can be more energy-efficient without sacrificing the ability to perform when needed. By intelligently managing CPU cores, we can achieve better battery life while maintaining responsiveness.

Give Observer a try and let me know your experiences. I'm particularly interested in hearing about battery life improvements on different hardware configurations.

Remember to star the repository if you find it useful, and feel free to open issues or contribute to the project!


This post was written on January 5, 2024. Observer is under active development, and new features may have been added since this publication.