powerd++  0.4.4
powerd++ Documentation
/\ __
__ __ ___ ___ __ _____ ___ ___/ /_/ /_
/_//_// \/ \/ // / _ \/ __\/ /_ _/__
__ __ / // / // / / ___/ / / // / /_/_/ /_
/_//_// ___/\___/\_/_/\___/_/ \___/ /_ _/
/ / /_/
\/ multi-core CPU clock daemon for FreeBSDĀ®

The powerd++ daemon is a drop-in replacement for FreeBSD's native powerd. Its purpose is to reduce the energy consumption of CPUs for the following benefits:

  • Avoid unnecessary fan noise from portable devices
  • Improve the battery runtime of portable devices
  • Improve hardware lifetime by reducing thermal stress
  • Energy conservation

Contents

  1. Using powerd++
    1. Packages
    2. Running powerd++
    3. Manuals
    4. Tuning
    5. Reporting Issues / Requesting Features
  2. Building/Installing
    1. Building
    2. Installing
    3. Documentation
  3. Development
    1. Design
    2. License

Using powerd++

Powerd++ offers the following features:

  • Load target based clock frequency control
  • Tunable sampling with moving average filter
  • Load recording and replay tooling for benchmarking, tuning and reporting issues
  • Command line compatibility with powerd(8)
  • Temperature based throttling
  • Expressive command line arguments with units, ranges and argument chaining
  • Helpful error messages
  • Comprehensive manual pages

Packages

The FreeBSD port is sysutils/powerdxx, the package name powerdxx.

Running powerd++

It is not intended to run powerd++ simultaneously with powerd. To prevent this powerd++ uses the same default pidfile as powerd:

# service powerdxx onestart
Starting powerdxx.
powerd++: (ECONFLICT) a power daemon is already running under PID: 59866
/usr/local/etc/rc.d/powerdxx: WARNING: failed to start powerdxx

So if powerd is already setup, it first needs to be disabled:

# service powerd stop
Stopping powerd.
Waiting for PIDS: 50127.
# service powerd disable
powerd disabled in /etc/rc.conf

Afterwards powerd++ can be enabled:

# service powerdxx enable
powerdxx enabled in /etc/rc.conf
# service powerdxx start
Starting powerdxx.

Manuals

Comprehensive manual pages exist for powerd++ and its accompanying tools loadrec and loadplay:

> man powerd++ loadrec loadplay

The current version of the manual pages may be read directly from the repository:

> man man/*

The manual pages as of the last release can also be read online.

Tuning

Three parameters affect the responsiveness of powerd++:

  • The load target (refer to -a, -b and -n)
  • The polling interval (refer to -p)
  • The sample count (refer to -s)

The key to tuning powerd++ is the -f flag, which keeps powerd++ in foreground and causes it to report its activity. This allows directly observing the effects of a parameter set.

Observing the defaults in action may be a good start:

# powerd++ -f
power: online, load: 693 MHz, 42 C, cpu.0.freq: 2401 MHz, wanted: 1848 MHz
power: online, load: 475 MHz, 43 C, cpu.0.freq: 1800 MHz, wanted: 1266 MHz
power: online, load: 271 MHz, 43 C, cpu.0.freq: 1300 MHz, wanted: 722 MHz
power: online, load: 64 MHz, 43 C, cpu.0.freq: 768 MHz, wanted: 170 MHz
power: online, load: 55 MHz, 42 C, cpu.0.freq: 768 MHz, wanted: 146 MHz
power: online, load: 57 MHz, 42 C, cpu.0.freq: 768 MHz, wanted: 152 MHz
power: online, load: 60 MHz, 44 C, cpu.0.freq: 768 MHz, wanted: 160 MHz
power: online, load: 67 MHz, 42 C, cpu.0.freq: 768 MHz, wanted: 178 MHz
...

Note, the immediate high load is due to the load buffer being filled under the assumption that the past load fits the current clock frequency when powerd++ starts.

Reporting Issues / Requesting Features

Please report issues and feature requests on GitHub or to kamik.nosp@m.aze@.nosp@m.bsdfo.nosp@m.ren..nosp@m.de.

Build Issues

In case of a build issue, please report the build output as well as the output of make info:

> make info
VERSION="0.4.3+c8"
GITVERSION="0.4.3+c8"
GITHASH="8431d86abe7479a4c0a040c19551ff3fa2454ea1"
PKGVERSION=""
TARGETS="powerd++ loadrec loadplay libloadplay.so"
CXX="ccache c++"
CXXFLAGS="-O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic"
CXXVERSION="FreeBSD clang version 8.0.1 (tags/RELEASE_801/final 366581) (based on LLVM 8.0.1) Target
: x86_64-unknown-freebsd12.1 Thread model: posix InstalledDir: /usr/bin"
UNAME_A="FreeBSD AprilRyan.norad 12.1-STABLE FreeBSD 12.1-STABLE #1 ea071b9cb32(stable/12)-dirty: Mo
n Oct 28 23:37:31 CET 2019 root@AprilRyan.norad:/usr/obj/S403/amd64/usr/src/amd64.amd64/sys/S403
amd64"

Performance Issues

If powerd++ behaves in some unexpected or undesired manner, please mention all the command line flags (e.g. from /etc/rc.conf powerdxx_flags) and provide a load recording:

> loadrec -o myissue.load

The default recording duration is 30 s. Do not omit the -o parameter, printing the output on the terminal may create significant load and impact the recorded load significantly.

Before submitting the report, try to reproduce the behaviour using the recorded load:

> loadplay -i myissue.load -o /dev/null powerd++ -f
power: online, load: 224 MHz, cpu.0.freq: 768 MHz, wanted: 597 MHz
power: online, load: 155 MHz, cpu.0.freq: 768 MHz, wanted: 413 MHz
power: online, load: 85 MHz, cpu.0.freq: 768 MHz, wanted: 226 MHz
power: online, load: 29 MHz, cpu.0.freq: 768 MHz, wanted: 77 MHz
power: online, load: 23 MHz, cpu.0.freq: 768 MHz, wanted: 61 MHz
...

Building/Installing

The Makefile offers a set of targets, it is written for FreeBSD's make(1):

Target Description
all Build everything
info Print the build configuration
debug Build with CXXFLAGS=-O0 -g -DEBUG
paranoid Turn on undefined behaviour canaries
install Install tools and manuals
deinstall Deinstall tools and manuals
clean Clear build directory obj/
releasetest Attempt a build and install from a gitless repo clone
testbuild Test build with a set of compilers
tb Alias for testbuild
doc Build HTML documentation
gh-pages Build and publish HTML and PDF documentation

Building

The all target is the default target that is called implicitly if make is run without arguments:

> make
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c src/powerd++.cpp -o powerd++.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c src/clas.cpp -o clas.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c src/utility.cpp -o utility.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -lutil powerd++.o clas.o utility.o -o powerd++
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c src/loadrec.cpp -o loadrec.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic loadrec.o clas.o utility.o -o loadrec
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c src/loadplay.cpp -o loadplay.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic loadplay.o clas.o utility.o -o loadplay
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -fPIC -c src/libloadplay.cpp -o libloadplay.o
c++ -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -lpthread -shared libloadplay.o -o libloadplay.so
>

The debug and paranoid flags perform the same build as the all target, but with different/additional CXXFLAGS. The debug and paranoid targets can be combined.

make testbuild / make tb

The testbuild target builds all supported test builds, the list of builds can be queried from the TESTBUILDS make variable:

> make -VTESTBUILDS
clang++90 clang++80 clang++70 g++9

A specific test build may be selected by appending it to the testbuild target:

> make tb/g++9
[testbuild/g++9]: make
g++9 -O2 -pipe -march=haswell -std=c++17 -Wall -Werror -pedantic -c ../src/powerd++.cpp -o powerd++.o
...

Instead of creating the default target any non-documentation target may be appended to the testbuild target:

> make tb/g++9/clean
[testbuild/g++9]: make clean
rm -f *.o powerd++ loadrec loadplay libloadplay.so

In order to run a specific target on all test builds, the build can be omitted from the target:

> make tb/clean
[testbuild/clang++90]: make clean
rm -f *.o powerd++ loadrec loadplay libloadplay.so
[testbuild/clang++80]: make clean
rm -f *.o powerd++ loadrec loadplay libloadplay.so
[testbuild/clang++70]: make clean
rm -f *.o powerd++ loadrec loadplay libloadplay.so
[testbuild/g++9]: make clean
rm -f *.o powerd++ loadrec loadplay libloadplay.so

Installing

The installer installs the tools and manual pages according to a recipe in pkg/files. The following variables can be passed to make install or make deinstall to affect the install destination:

Variable Default
DESTDIR
PREFIX /usr/local
DOCSDIR ${PREFIX}/share/doc/powerdxx

DESTDIR can be used to install powerd++ into a chroot or jail, e.g. to put it into the staging area when building a package using the FreeBSD ports. Unlike PREFIX and DOCSDIR it does not affect the installed files themselves.

Documentation

Building the documentation requires doxygen 1.8.15 or later, building the PDF version of the documentation requires xelatex as provided by the tex-xetex package.

The doc target populates doc/html and doc/latex, to create the PDF documentation doc/latex/refman.pdf must be built.

The gh-pages target builds the HTML and PDF documentation and drops it into the gh-pages submodule for publishing on github.io.

Development

The following table provides an overview of repository contents:

File/Folder Contents
doc/ Output directory for doxygen documentation
doxy/ Doxygen configuration and filter scripts
gh-pages/ Submodule for publishing the documentation
man/ Manual pages written using mdoc(7) markup
obj/ Build output
pkg/ Installer scripts and instructions
loads/ Load recordings useful for testing
src/ C++ source files
src/sys/ C++ wrappers for common C interfaces
powerd++.rc Init script / service description
LICENSE.md ISC license
Makefile Build instructions
README.md Project overview

Design

The life cycle of the powerd++ process goes through three stages:

  1. Command line argument parsing
  2. Initialisation and optionally printing the detected/configured parameters
  3. Clock frequency control

The first stage is designed to maximise usability by providing both, the compact short option syntax (e.g. -vfbhadp) as well as the more self-descriptive long option syntax (e.g. --verbose --foreground --batt hiadaptive).

The second stage is designed to trigger all known error conditions in order to fail before calling daemon(3) at the start of the third stage. Both the first and second stage are meant to provide specific, helpful error messages.

The third stage tracks the CPU load and performs clock frequency control. It is designed to provide its functionality with as little runtime as possible. This is achieved by:

  • Using integer arithmetic only
  • Minimising branching

The latter is achieved by using function templates to roll out possible runtime state combinations as multiple functions. A single, central switch/case selects the correct function each cycle. This basically rolls out multiple code paths through a single function into multiple functions with a single code path.

The trade-off made is for runtime over code size. With every bit of state rolled out like this the number of functions that need to be generated doubles, thus this approach is limited to the few bits of state that control the most expensive functionality, e.g. the foreground mode.

License

This project is published under the ISC license.