Battery Life Still Isn't Great

Hey all.

I used to get around 12 - 14 hours of battery life on idle with my old Debian 12 Stable install. With NixOS, on idle I get 9 hours, and in use I get 3 - 4.

I’ve followed a lot of the battery tweaks available on the forum, as well as online.
Including:

  • Tweaking kernelparams.
  • Using my device’s hardware on nixos-hardware (I couldn’t find the precision 5540, but found the 5530, and that’s been working).
  • Using TLP & Auto-CPUFreq (I know it’s not recommended to use both at the same time, but that has given me better battery life than just using only one).
  • Using ThermalD
  • Enabling All Firmware hardware.enableAllFirmware = true;
  • Using the linux zen kernel.
  • EnvyControl to use only integrated graphics.

Here’s my battery.nix file, I am importing this into configuration.nix and using nix flakes.

{ inputs, outputs, lib, config, pkgs, ... }:
{
  ## POWER
  powerManagement = {
    enable = true;
    #cpuFreqGovernor = "schedutil";
  };
  services.power-profiles-daemon.enable = false;
  services.auto-cpufreq.enable = true;
  services.auto-cpufreq.settings = {
	  battery = {
	     governor = "powersave";
	     turbo = "never";
	  };
	  charger = {
	     governor = "performance";
	     turbo = "auto";
	  };
	};

  ### KERNEL
  boot.kernelParams = [
    "ahci.mobile_lpm_policy=3"
    "rtc_cmos.use_acpi_alarm=1"
  ];

  ### HWP
  systemd.tmpfiles.rules = [
    "w /sys/devices/system/cpu/cpufreq/policy*/energy_performance_preference - - - - balance_power"
  ];

  ### TLP
  services.tlp = {
      enable = true;
      settings = {
        CPU_SCALING_GOVERNOR_ON_AC = "performance";
        CPU_SCALING_GOVERNOR_ON_BAT = "powersave";

        CPU_ENERGY_PERF_POLICY_ON_AC = "performance";
        CPU_ENERGY_PERF_POLICY_ON_BAT = "power";

        PLATFORM_PROFILE_ON_AC = "performance";
        PLATFORM_PROFILE_ON_BAT = "low-power";

        CPU_BOOST_ON_AC=1;
        CPU_BOOST_ON_BAT=0;

        CPU_HWP_DYN_BOOST_ON_AC=1;
        CPU_HWP_DYN_BOOST_ON_BAT=0;


        #CPU_MIN_PERF_ON_AC = 0;
        #CPU_MAX_PERF_ON_AC = 100;
        #CPU_MIN_PERF_ON_BAT = 0;
        #CPU_MAX_PERF_ON_BAT = 20;

       #Optional helps save long term battery health
       START_CHARGE_THRESH_BAT0 = 60; # 60 and below it starts to charge
       STOP_CHARGE_THRESH_BAT0 = 90; # 90 and above it stops charging

      };
  };

  ### SYSTEM 76 SCHEDULER
  services.system76-scheduler.settings.cfsProfiles.enable = true;

  ### POWERTOP
  #powerManagement.powertop.enable = true;

  ### ThermalD
  services.thermald.enable = true;
}

I am using:

Dell Precision 5540
NixOS-Unstable
91 Whr Battery
EnvyControl (sudo envycontrol -s integrated, for running off only integrated graphics)

Neofetch:
          ▗▄▄▄       ▗▄▄▄▄    ▄▄▄▖            amon@nixos 
          ▜███▙       ▜███▙  ▟███▛            ---------- 
           ▜███▙       ▜███▙▟███▛             OS: NixOS 24.05.20240306.9df3e30 (Uakari) 
            ▜███▙       ▜██████▛              Host: Dell Inc. 0FMYX6 
     ▟█████████████████▙ ▜████▛     ▟▙        Kernel: 6.7.7-zen1 
    ▟███████████████████▙ ▜███▙    ▟██▙       Packages: 1721 (nix-system), 681 (nix-user), 8 (flatpak) 
           ▄▄▄▄▖           ▜███▙  ▟███▛       Shell: zsh 5.9 
          ▟███▛             ▜██▛ ▟███▛        Resolution: 1920x1080, 2560x1440 
         ▟███▛               ▜▛ ▟███▛         WM: dwm 
▟███████████▛                  ▟██████████▙   Theme: WhiteSur-Dark [GTK2/3] 
▜██████████▛                  ▟███████████▛   Icons: WhiteSur-dark [GTK2/3] 
      ▟███▛ ▟▙               ▟███▛            Terminal: kitty 
     ▟███▛ ▟██▙             ▟███▛             Terminal Font: monospace 11.0 
    ▟███▛  ▜███▙           ▝▀▀▀▀              CPU: Intel i9-9880H (16) @ 2.300GHz 
    ▜██▛    ▜███▙ ▜██████████████████▛        GPU: NVIDIA Quadro T2000 Mobile / Max-Q 
     ▜▛     ▟████▙ ▜████████████████▛         GPU: Intel CoffeeLake-H GT2 [UHD Graphics 630] 
           ▟██████▙       ▜███▙               Memory: 2.77GiB / 31.00GiB 
          ▟███▛▜███▙       ▜███▙              GPU Driver: Dell Device [1028:0906] 
         ▟███▛  ▜███▙       ▜███▙
         ▝▀▀▀    ▀▀▀▀▘       ▀▀▀▘                                     
                                                                      

Could also try https://github.com/NixOS/nixos-hardware/blob/59e37017b9ed31dee303dbbd4531c594df95cfbc/common/gpu/nvidia/disable.nix, a bit of a bigger hammer. Generally the dedicated graphics will be the biggest battery hog.

3 Likes

Hello Locbac!
From what i read in your config, i would try 2 things:

1 - I would blacklist the intel pstate kernel module and use acpi-cpufreq as sugested in the auto-cpufreq readme under the toubleshooting section
A: If you’re using the intel_pstate/amd-pstate CPU management driver, consider changing it to acpi-cpufreq.

2 - I would disable the nvidia gpu completely (for troubleshooting purposes). I have experienced a constant 10W draw from it with nouveau or proprietary, and it did draw 10W constantly(never tried envycontrol myself) but after uninstalling the drivers and blacklisting the nouveau module it basically turned off and my battery life improved.

i have a dell inspiron wiht a skylake i7 and a geforce 930M gpu, not at all comparable hardware, especially due to age, but these steps did seem to help my battery life.

{ config, ... }: {

  powerManagement = {
    enable = true;
    powertop.enable = true;
    cpuFreqGovernor = "powersave";
  };
  services = {
    thermald.enable = true;
    power-profiles-daemon.enable = false;
    auto-cpufreq = {
      enable = true;
      settings = {
        battery = {
          governor = "powersave";
          turbo = "never";
        };
        charger = {
          governor = "powersave";
          turbo = "auto";
        };
      };
    };
    system76-scheduler = {
      enable = true;
      useStockConfig = true;
    };
    udev.extraRules = ''
      # Remove NVIDIA USB xHCI Host Controller devices, if present
      ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{power/control}="auto", ATTR{remove}="1"
      # Remove NVIDIA USB Type-C UCSI devices, if present
      ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{power/control}="auto", ATTR{remove}="1"
      # Remove NVIDIA Audio devices, if present
      ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{power/control}="auto", ATTR{remove}="1"
      # Remove NVIDIA VGA/3D controller devices
      ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", ATTR{power/control}="auto", ATTR{remove}="1"
    '';
  };
  boot = {
    extraModprobeConfig = ''
      blacklist nouveau
      options nouveau modeset=0
    '';
    blacklistedKernelModules =
      [ "nouveau" "nvidia" "nvidia_drm" "nvidia_modeset" ];
  };
}

there is my config if you are courious… I get 6 to 8 hours on this laptop from 2016 with the original battery still…

I will dive deeper in the system76 scheduler config soon, but not yet. it didnt improve my batterylife, however it did improved my performance without worsening y batterylife, which actually surprised me…

I don’t think that the kernel params bit will help you as much as you think they will, especially since you most likely are using an ssd, but sure and sometimes I disable powertop aswell, don’t know why you did it, but for me are usb keyboards… a pain to type sometimes.

And in my opinion you are messing too much and in too many with the governor and with the policy and profiles, and the boost… I don’t think this is a good idea, because it can lead to some unforesseen problems and conflicts, in linux everything is a file, your current freq is set through a file, and every other setting… sure it is write protected from you, but for it to change it is re-written. maybe you have something thats writting a thing and another process overwrites it…

I’m not saying it will be a problem, i’m saying it may be one. Not really about battery life, but about stability and reliability…

2 Likes

Hey Locbac, any updates? how is battery life now?

1 Like

I could probably learn a lot from the suggestions here. For now I just take a big hammer and disable nvidia dgpu entirely, as well as limit CPU_MAX_PER_ON_BAT=60 in tlp, while CPU_BOOST_ON_BAT=0 and CPU_HWP_DYN_BOOST_ON_BAT=0.

Very inelegant. Kinda works. :stuck_out_tongue:

When it comes to battery life, the only sensible thing to do is measure, measure, measure.

Battery life is inversely proportional to power draw. Thus, you must monitor power draw of your laptop. Some batteries’ firmware allows doing that but if yours doesn’t, you need to resort to fully charging the laptop and then using a power meter on the charger.

What you must understand going into this is that what matters most on a laptop is the idle power draw because (from the perspective of the system) your laptop will be idle most of the time.
Go measure that. It should be in the single digit watts depending on display brightness setting.

If it’s not, you must go into powertop and investigate. Both CPU and, equally important, chipset/package should be mostly in the highest C-states they can be. If they’re not, there’s something preventing them from doing so which can be a lot of things:

  • A process on your system
  • PCI
  • USB
  • SATA

These are usually the primary causes for power draw in a laptop used for light tasks.

Powertop allows you to toggle power management for each individual device. Go enable it on a few of them, measure again and look at C-states again until your CPU and package mostly reside in the highest C-states. (You have to look up how high your platform can go, it often shows more than there actually are.)

Once you have identified which ones cause the low C-states, TLP can be configured to enable power management on them automatically.

Next get rid of scheduler tweaks, governors or disabling turbo. They’re usually ineffective at achieving good battery life. Use the default scheduler with either schedutil or performance governor and keep turbo on.

Next, you need to figure out whether you even want to control power draw under load because, as I said, most of the power draw usually happens at idle, not under load.
If you do want to limit how much power your CPU draws under load, the tool to use is power limits: Your CPU’s firmware can directly control the CPU’s power draw; you only need to tell it to. Tools like undervolt can set PL1 and PL2 aswell as their durations to whatever you like.

(I should write a blog post about this…)

6 Likes

Battery life is somewhat similar, not many major changes during use, however during idle it does creep up to 12 hours of battery life.

Powertop doesn’t seem to give me power draw readings, and only events per second.

PowerTOP 2.15     Overview   Idle stats   Frequency stats   Device stats   Tunables   WakeUp                            

The battery reports a discharge rate of 9.78 W
The energy consumed was 241 J
The estimated remaining time is 4 hours, 58 minutes

Summary: 1766.4 wakeups/second,  0.0 GPU ops/seconds, 0.0 VFS ops/sec and 64.4% CPU use

                Usage       Events/s    Category       Description
             19.3 ms/s     1214.5       Timer          tick_nohz_highres_handler
              1.0 ms/s     146.5        Interrupt      [17] idma64.1
            134.5 ms/s      59.7        Process        [PID 3285] /nix/store/la3q4nvrvsfdnmapw1ixgj0py8q9m6a9-firefox-123.0.1/bin/.firefox-wrapped
            497.1 µs/s      81.3        kWork          rps_work
            175.0 ms/s       0.8        Process        [PID 5749] /nix/store/la3q4nvrvsfdnmapw1ixgj0py8q9m6a9-firefox-123.0.1/lib/firefox/firefox -contentproc -childID 17 -isForBrows
             24.5 ms/s      18.5        Process        [PID 2798] picom -b --backend glx --window-shader-fg /home/amon/grayscale.glsl
             19.3 ms/s      20.1        kWork          intel_atomic_commit_work
            186.8 µs/s      23.1        kWork          blk_mq_run_work_fn
             54.5 ms/s       0.5        Process        [PID 3364] /nix/store/la3q4nvrvsfdnmapw1ixgj0py8q9m6a9-firefox-123.0.1/bin/.firefox-wrapped
            364.0 µs/s      20.2        kWork          intel_atomic_cleanup_work
            449.4 µs/s      19.9        Process        [PID 17] [rcu_preempt]
              7.1 ms/s      17.2        Interrupt      [0] HI_SOFTIRQ
              2.2 ms/s      18.6        Timer          hrtimer_wakeup
             32.1 ms/s       3.3        Process        [PID 2496] /nix/store/hf4rbbcdzgl1nbz4nv8hgwjjl7q8flnn-xorg-server-21.1.11/bin/X vt2 -displayfd 3 -auth /run/user/1000/gdm/Xaut
              2.4 ms/s      14.4        kWork          engine_retire
              1.2 ms/s      12.7        Process        [PID 1412] [irq/51-SYNA2393]
            123.9 µs/s      10.3        Process        [PID 18847] /nix/store/w7s5i4hxgfhazdqifva78hk1ydrmhykv-syncthing-1.27.2/bin/syncthing -no-browser -gui-address=127.0.0.1:8384
            346.6 µs/s       8.3        kWork          __i915_gem_free_work
            404.5 µs/s       0.8        Process        [PID 25526] [kworker/8:0]
1 Like

It says right there:

That’s the most you’ll get. Components don’t individually measure their power; every reading you see regarding this is an estimate and usually wrong.

Assuming that represents idle, that’s not the worst (depending on the brightness) but it could likely be a few watts lower.

You need to navigate to the other tabs for C-states (“Idle stats”) and enabling device power management (“Tunables”).

1 Like

Next get rid of scheduler tweaks, governors or disabling turbo. They’re usually ineffective at achieving good battery life. Use the default scheduler with either schedutil or performance governor and keep turbo on.

My naive assumption was that disabling turbo and reducing maximum cpu frequency should help with battery life, but is this not true?

you’ll have to go deeper on the powertop to find out what is drawing power, as @Atemu said, individual components do not report their power draw… the tunables section is a good start and the device stats might shed some light, specially if you are able to reinstall your debian and compare the devices usage, events and calls, but again, i’ll be a lot of trouble to find it out… I personally would accept what i’m able to get and maybe use specialisations to enable and disable a performance and a power-save mode, the downside of this aproach is that you need to reboot depending on the configuration you make…

If your idle power draw was, say, 10W, a boost to 30W for a second because you loaded a web page would only reduce battery life by 2s worth of idle time.
If it hadn’t boosted to 30W for a second though, it would have had a less elevated but still elevated power draw. Without any boost whatsoever I’d estimate 15-20W for a single core load. Because the clocks were so much lower, it’d have to sustain those for a longer time however.

It’s not entirely true but assuming performance scales linearly with power draw, that’d mean you’d need twice as long for the page load which not only sucks from a UX perspective but also means the CPU would have to sustain 20W for 2s rather than 30W for 1s. That’s still 20Ws more energy drawn than idle would have drawn (40Ws vs 20Ws) and therefore still 2s less battery life.

Now, this hinges on some assumptions that don’t hold in the real world, so not boosting does usually cause less draw even with spiky loads but what I wanted to show is that it’s not a whole lot. Certainly not worth the significantly worse UX.

Where disabling boost actually helps significantly is sustained loads but there it’s effectively just a worse power limit, so… simply set power limits instead.

We’re talking about seconds here though while battery life on a lightly used laptop is measured in hours. What’s a 30W spike every now and then when it constantly draws 10W for hours on end? Imagine how you use your laptop. Is it under full load all the time? Most likely not. In the time you’ve thought of how to word your Google search, you will have drawn more energy than the boost for the Google page load would have.

Your laptop is idle most of the time and thus idle power consumption is the most important.

If that is not the case and your laptop is indeed under significant load for extended periods of time, the appropriate tool is a power limit as I mentioned as that still allows bursty boosts and better allocation of power to individual cores.

1 Like

We recently implemented various kernel improvements for battery savings: kernel/common-config: enable a whole bunch of stuff by K900 · Pull Request #296867 · NixOS/nixpkgs · GitHub after I realized that I was drawing too much power on my laptop and I wanted to have good battery life.

1 Like

Ok, how would I get this kernel? Should I switch back to the regular linux kernel and not linux-zen?

It will be in linux-zen too; what matters whether your Nixpkgs revision has the kernel config change yet.

Unfortunately that depends, some programs are just utilizing whatever resources they can get. For example when you are on a video call, most programs will try to utilize all available resources. Might be 30W, but often the quality isn’t too bad if you constrain the resources to 10W - but the desired improvement over battery life is real.

Will these come to the default linux lts package?

The configs will apply to all of the kernel packages once they land in the channels

2 Likes

Please do, I think good information on this topic is lacking.

2 Likes

The cause is probably not correct but I would not be surprised if that effect was real in some circumstances; video calls requiring more power with boost than without.

This is rather irrelevant though because if you didn’t want to continuously draw a large amount of power, the appropriate tool would still be an explicit long-term power limit (PL1 for Intel) rather than the implicit power limit that disabling boost technically provides as a side-effect.

1 Like

I tried some suggestions on this thread and disabling services.power-profiles-daemon.enable = true; on a dell precision 3551 ended up with fan always on. Had to revert swiftly just as a datapoint.

1 Like