When writing code for embedded systems, when processor power efficiency is a very important factor, compiler optimizations are one very important method of writing efficient code.
Understanding compiler optimizations can help you write more efficient code that runs important operations faster, reducing the power usage of your embedded systems and thus extending their battery life. In addition to the importance of addressing battery life, compiler optimizations reducing runtime can also make the difference between an algorithm that can be implemented in the embedded space and one that cannot.
The first compiler optimization one can make is to appropriately select the data type size. Embedded C’s primary and most efficient data type is the integer. While you could use a local character variable (8 bits) and increment its value, internally in both CISC and RISC architectures, this would result in assembly code which internally promotes the character to an integer, increments the value, and then truncates the result back to a character. Thus, two extra instructions are typically necessary. In addition to tripling the cost of this operation behind the scenes, you’re also not saving memory on any modern 32- or 64-bit processor by using smaller data types. While many textbooks recommend always using smaller data types, many of these were written in the age of embedded 8- or 16-bit processors. This rusty advice can now safely be retired.
The second compiler optimization for embedded system code is to use unsigned variables when appropriate, as the use of unsigned variables is more efficient for certain operations. One particular useful example is division of an unsigned variable by a power of two can be performed in a single right shift bit operation. Using this simple trick and others like it can save 16 or more clock cycles.
A third compiler optimization is to make use of access types. For global data, the static keyword (or for embedded C++ the anonymous namespace) should be used as frequently as possible as it allows the compiler to make intelligent optimizations; however, do not use static for function-local data. This is a common performance pitfall in the embedded space as it requires the local variables value to be maintained between function calls.
While use of access types such as static are important, one common myth for writing efficient C code is that use of const enables compiler optimizations in both C and C++. Const does not make C code faster. See this article for a detailed description of why.
Another important compiler optimization is to make appropriate use of pointers and the & operator. One should usually return scalar values from a function instead of returning a scalar value by reference or address. In other words, avoid passing scalar references as output arguments to a function. Note that this is only for scalar values, it is almost always a better idea to pass objects and arrays as references as passing by value would result in an extensive number of copy operations.
At Robotiquality, our System Engineers are expert in writing efficient embedded code. Contact us for help with all of your embedded IIoT needs, including how to not only write embedded C, but to use other languages such as Rust.