PowerControl: Improve the Performance of Your Nerves Device

By: Chris Freeze
Light bulb hovering in a hand

I’d like to introduce PowerControl, a Nerves library that allows you to control the clock speed of your embedded devices and disable components you may not be using to lower a device’s energy consumption.

Nerves

Nerves is an open source software suite for developing and deploying Elixir applications to embedded devices. It’s great for IoT (Internet of Things) projects and other smart devices. The fault-tolerance of Elixir’s OTP really shines in an embedded environment, and Nerves gives you the tools to easily develop and deploy to your device.

Motivation

I was given the idea to write this library after a discussion about Nerves with Frank Hunleth, who mentioned he was manually doing cpu speed management and other power-saving things in many of his projects and wished there was a library which could automate the process. I decided to try my hand at writing such a library to increase my knowlege about some of the lower level features of embedded devices.

Currently, PowerControl can successfully lower your device’s energy footprint, and can do so selectively by turning off what is not in use. It also has the ability to change CPU clock speed to adaptive or max performance modes, or even just keep it on a power-saving mode. It does all this at startup when integrated with Shoehorn, and it also lets you change these settings during runtime by simply calling an Elixir function.

Reduce your energy footprint

A reduced energy footprint has a number of advantages, both for companies with embedded systems and for the environment. Reducing energy usage saves money. Even a few milliamps per device can add up over the years, especially if you’re running an operation with hundreds or even thousands of devices. If those devices are vehicle-based, you’re saving fuel which, depending on your area, can have even more of an impact over time. In addition to cutting costs, it also cuts emissions. Less power used means less coal and fuel burned. It helps the environment at no cost to yourself. It can even save costs by increasing the lifespan of your device, keeping electronic waste out of landfills.

Increase performance

CPU speed management is done on Buildroot-based embedded devices (and many other unix systems) with CPU governors. CPU governors control CPU clock speed according to their documentation, and can be as simple as always minimizing or maximizing, or setting the clock speed conditionally based on factors such as load and temperature. For more information on CPU governors and how the linux CPUFreq settings work, see the docs here. Currently, the default governor for all Nerves devices is powersave, which results in the lowest clock speed at all times. With PowerControl, you have easy access to other governors, like performance to maximize the clock speed. Other governors also exist to selectively change the clock speed based on load or temperature. This control is useful for embedded devices doing time-sensitive data processing; if you were using Nerves devices for a processing-intensive use case before now, it’s possible you were missing out on some performance. The library allows for configuring the governor at startup and runtime. A possible use case could be creating a web interface which allows you to set the governor for your fleet of devices at your leisure.

Challenges

Accomplishing these features and goals required learning about some really interesting subjects I had no previous experience with, such as Linux documentation and sysfs. The most challenging part of writing the library was digging through Linux and Raspberry Pi docs to find out which power-consuming components I could configure and how to do so. This took me several hours over the course of several days. I found that most could be configured by writing to a file or running a shell command, which didn’t require a lot of code. As a result the library itself is very slim. That being said, I’m certain there are things I missed that could save even more power, so PRs are appreciated.

Benchmarks

Now that I’ve introduced the library, time for the fun stuff. I performed several benchmarks on my library to test energy consumption and performance. For energy consumption I was lucky enough to be given a prototype of a device called the JouleScope, which functions as a multimeter, oscilliscope, and power supply you can use to power and monitor energy usage of embedded devices (or really any electric device up to quite a few amps). Using this device, I tested various configurations of my library against a control—in this case, a default Nerves app—recording their energy consumption for three minutes for three trials. I did this having each application sit idle, and also put under heavy CPU load in the form of four workers that endlessly calculate large fibonacci numbers.

First, the idle results: idle graph

Trial set A was performed with the theoretical highest power saving settings, and the results definitely match that. Compared to the control, we see power savings of .08 Watts. We can also interpret from this graph that the primary consumer of power on a Raspberry Pi Zero is unsurprisingly the CPU, as the power savings gained from disabling unused components does not completely counteract the power cost of maximizing your CPU clock speed.

Next, the load benchmark:

Load graph

No surprises here either. The power savings from disabling unused components are almost the same as during the idle tests, but slightly higher. For now I conclude this is just due to data precision limits, but future testing done with even more savings may change that conclusion. The cpu is once again the main consumer here.

Finally, the performance benchmarks:

Load graph

Interesting data pattern aside, this is as expected. A faster clock results in a shorter execution time. Up until now we’ve focused only on the governors powersave and performance, but other governors exist. The governors conservative and ondemand increase cpu clock speed when usage is high, though follow different algorithms to decide when to do so. The following chart shows power usage and execution time when intermittantly calculating a large fibonacci number:

performance vs consumption

To gather the data for this chart, the code was set up so that the pi would calculate as many fibs as possible with five-second breaks between calculations in an attempt to simulate a real-world scenario where the device would not be busy 100% or 0% of the time. The results are interesting in that the conservative governor showed near-performance execution times with slightly lower power usage. From looking at the data it is clear that the difference in power usage between performance and conservative correlates with the length of the idle-time between calculations, meaning that the conservative governor is an excellent choice if you don’t want to sacrifice performance for power savings. As a fun bonus, here is a JouleScope reading of the current draw of the same setup used for the previous data with the conservative governor configured:

conservative governor scope reading

The y axis here is current draw in amps and the x axis is seconds. The red lines represent the minimum and maximum of the various current readings taken in the specific timeslice, and the yellow is the the mean of all the readings. It’s interesting that the cpu clock speed increase happens in two distict steps. We can see here that the pi takes around .1-.3 seconds to complete its stepping up of the clock speed. Knowing this, it is clear that with longer busy-times and longer idle-times the conservative governor can approach the power-consumption levels of powersave and the execution times of performance.

Conclusion

The aim of PowerControl is to give Nerves developers easy access to power and CPU configuration options on their embedded devices. In its current state, the library lets you control CPU clock speed and behavior, and it gives you easy access to disabling unused components to save energy. I consider it to be functional and safe for production, but still in early development. There are many more features which I am currently investigating to add, such as wireless networking, disk, and USB power settings. I intend for the library to be a hub where the best practices of interacting with devices for power control can be accumulated and used, so if you are interested feel free to do your own power saving sleuthing and create a PR.

Thanks

I’d like to thank Frank Hunleth for giving me the idea for the library and helping out with Linux documentation, and Matt Liberty for giving me early access to JouleScope to do the measurements. Check out the JouleScope Kickstarter here. Please feel free to request features or report bugs with my library on the github page here.

DockYard is a digital product agency offering exceptional strategy, design, full stack engineering, web app development, custom software, Ember, Elixir, and Phoenix services, consulting, and training.