In part one of this series I talked about moving more digital signal processing back to the analog domain to save energy. The savings is accomplished by both reducing processor cycles and by moving the functions to lower power analog solutions. But what about the software code itself? Does it have energy content and can it be optimized to reduce a system’s power consumption? The answer to all of these questions is YES.
To prove the point you can examine any modern laptop computer architecture. Laptop processors are designed to speed up and slow down depending on the system load. If you are simply idling the CPU by typing a blog post (like I’m doing while I write this), the processor doesn’t need to run at its full processing capacity. Likewise, the operating system is aware of the processes that are running and adjusts the system accordingly. All of these advancements provide much longer run times in today’s portable computers.
However, fundamentally the software industry has not embraced the concept of "Code Energy" or how many joules a subroutine or algorithm uses. Object oriented languages have enabled modular code reuse and tremendously improved the time it takes to create software. In fact, it’s so easy even this author can create fairly powerful software without spending 20 years perfecting my skill. This arguably can result in poorly written code - software that is wasteful in both CPU cycles and energy.
For example, many GUI (Graphical User Interface - pronounced "gooie") routines like a list update can be handled in several ways. The correct way is to suspend the graphical refresh thread while updating the list. This action prevents the GUI from continuously trying to refresh the visible list the user sees while the processing thread adds contents to the list. I see this error many times in software and it’s very annoying. It wastes cycles and slows down the user interface - sometimes to a crawl... See my Delphi example below of a correct way to update a ListBox object:
Another example is bloated code. Many times libraries of functions are used to simplify the creation of a software project. This is an extremely powerful concept allowing great reuse of routines. These libraries can be dynamically loaded or unloaded from memory as needed and can be shared among many programs or processes running simultaneously. Now imagine you have a library that contains a quick sort routine as well as 100 other routines all packaged into one library (for Windows fans, a DLL or Dynamic Link Library). This is a precompiled object that stands completely alone and is loaded by calls to the operating system. If all you wanted is the quick sort routine, the operating system would need to load (and maintain) the entire library of routines since they are a complete unit. This takes cycles, memory and other resources. This is the equivalent of buying (and storing somewhere) the entire contents of a clothing shop when all you wanted was a single T-Shirt.
Many software engineers have become complacent with the belief that they’ll always have enough memory, disk space or CPU cycles. The tools for creating software today are incredibly powerful and allow amazing amounts of code reuse and optimization. It is very easy to become dependant on the tools to make sure our code is written efficiently. The tool, like a hammer or screw driver, is only as good as the user at the control end. Poor architecture leads to poor performance and thus, high energy content.
If you turn back the hands of time 30 years to the day when microcontroller systems had 16KB (16,384 bytes) of RAM, engineers were exceptionally creative in building systems that ran efficiently. They didn’t have object-oriented language tools and any code that was reused was from routines written in ASSEMBLER for that particular processor - not a universal module that could be targeted to any system. In those days it took years to create complex systems, so with the advent of high-level object-oriented languages such as C++, engineers were quick to adopt them. Unfortunately, many of these tools were targeted toward code reusability and time to completion - not energy efficiency.
As it turns out, many of these inefficiencies were noted in the poor performance of early operating systems. The offending routines, usually in the kernel or core of the system, were re-written in highly efficient assembler code and optimized for quicker execution. This action, in affect, lowered the energy content of the OS kernel and improved the performance of the overall system. Engineers today don’t necessarily need to hand code every little routine in machine language to get better efficiency from their software. Let’s look at a list of things that can lower the energy content of software - all of these ideas apply to both embedded systems and full out computer systems and servers. I’m going to do them in "Letterman Style" ascending order of importance for fun and effect - now, no looking ahead!
10. Use a stop watch : If you want to know exactly how much energy is in your software, measure it. That is, if the process takes seconds to complete you have a great deal of processing going on. I like the comparisons of various sorting techniques - Bubble, Bi-Directional Bubble, Selection, Shell, Quick Sort and others. On large data sets you can actually time how long it takes for these routines to complete and compare one with the other... a stop watch is a handy programming tool. In this case, shorter is better...
9. Use a power meter : This is really important for embedded applications, but can apply to most systems. The power meter should be a chart recording type (digital or analog) and calibrated to the range of the system. You can actually watch when the supply power fluctuates due to sections of the system turning on or off or system clocks speeding up or slowing down. Oh, you don’t dynamically control the subsystems in your project to save energy? Bad programmer... what have we been talking about here?
8. Ask your colleagues : Has anyone in your department ever done something like this before? I’ve heard stories of engineers sitting one cube apart where one of them has worked on a similar project and the other never knew. Ask around your department before you start writing code - maybe someone perfected the ideal and highly optimized routine for you to use...
7. Think of CPU cycles as money : Imagine having one million dollars available and you need to build a project using those funds. What if, at the end of the project, any money left over was yours to keep? How would you spend the money while building the project - I’d imagine very frugally... Think of your computer cycles the same way. Remember, cycles are not free and neither is memory or communication bandwidth. These all have finite costs associated with them - and they all draw power.
6. Know your operating system : This is one of my biggest pet peeves! I cannot tell you how many times I’ve seen code written that intentionally works around the benefits built into the OS and runs completely unreliably or with abysmal performance - and thus, high energy content. I mentioned a few examples above, but the list could include hundreds of examples if someone gave me the time!
5. Learn your language : Just because you can write a simple hashing routine doesn’t mean you fully understand the programming language you are using. I, for one, don’t always know every OS call or library routine - and that’s all right since that’s what reference documents are for. However, you should know the best way to code a solution to a problem. Just like a written language, there are ways to communicate things that are more efficient and precise. I find badly written code so often it makes me wonder if these programmers ever went to school to learn how to write code! Poorly written code wastes energy...
4. Change your perspective : This is true for many problem-solving ventures. A new or fresh perspective can sometimes expose methods that are superior to those used in the past. I often find myself taking a coffee break, or simply walking around for a while when I’m looking for a software bug or before starting a new design. Have someone else look at it as well. I once had my wife look at some broken code (she is not an engineer or programmer) to see if she could find anything that seemed out of place. She noticed that when letters and numbers appeared in parentheses (an "if" test), there were always two equal signs... except in one. There was the problem - a valid line of code that didn’t do what I wanted! I had looked at that code for days and never saw the flaw.
3. Improve your hardware knowledge : Software engineers may sometimes feel that all they need to know is software. Knowing more about the effects of your code on the performance of the system is absolutely a requirement - no options here. For example, our cable company recently "upgraded" our set-top boxes with "new and improved" software. Not only does this new release lack important functions that were previously there, but the overall performance is much slower - and all on the same hardware as we’ve been using! Bad programmers...
2. Be modular : Once you’ve taken all the bloat out of your routines, clean up the entry and exit points to allow encapsulation. Why waste all that great coding? Even with my primitive coding skills, I’ve created routines that have simple entry and exit points. Some I never thought I’d use again. But every once in a while I find a great piece of code in my stash of routines which saves hours of re-engineering the same solution!
1. Understand your tools : I cannot stress this point enough! The tools that compile and assemble your high level object oriented code into machine code have many options to control the process. Sometimes you want to optimize for space and other times you may want to optimize for performance - these are trade-offs. The tool sometimes will include diagnostic code in the compile to make debugging easier... did you remember to take it out in the final distribution? I know these sound like simple things, but I’m sure there’s a large amount of production code out there that was released with the wrong flags set during the compile... we all get busy and forget things!
0. (Bonus) Always document EVERYTHING : You thought I was ranting on the tools issues - this is my biggest complaint when I look at some else’s code. If you did something clever - TELL ME... If you know that setting a bit in the processor during a particular operation lowers the power, make a note to why you did it. Encapsulate your routines as well. Tell me a story about why this routine exists - why was it important enough to encapsulate? Oh, I could rant on this all day, but this is about saving energy - not hair on my head (it’s too late for that now).
I’m sure you have similar rants and raves or simply good ideas on how to write efficient code. Do share if you have some! Hope you enjoyed this two-part series and gained some insights on how to lower the energy content of your software. Till next time...