Embedded C: Optimization

Most hard part of the coding is to optimize it for efficiency and minimizing resource usage. In embedded systems optimization becomes the highest priority because of the limited resources, so the program should be optimized to execute with less memory/storage, shrink power consumption and other resources.

Largely program optimization is targeted to achieve more execution speeds and consume less memory.  Compiler provides lot of tools to optimize your code and also tools to figure out the bottlenecks affecting the performance of the program. In spite of the enablers compiler provides, one should consider techniques that can be incorporated during coding to develop an efficient program. Below I will try to discuss some (not extensive) of the techniques I have adapted/learnt, which you may find useful:

Simple and clear code, and code for correctness

First and foremost important thing is to write a code which is easy to understand by humans and in a way also to compilers. And always focus on getting the program to work functionally correct first, if optimization is forced into earlier stages of coding then it may lead to understated bugs. You will have harder time debugging the issues, which are sometimes the offset of optimization done by compilers.

Start by optimizing functions/repeatable section first

If there is a function which is 50% of the run time, and if you optimize the same to bring the run time to 25% you already have the program which is 50% faster. So it’s good to always optimize the repeatable/critical sections more than sections which are not executed often. It’s good to trade off the optimization of rare case with common case.

Declare right data types for variables

If the focus of optimization is for the execution time, then always choose the datatypes that are native to MUC/Processor. For 32 bit MUC, variables of char data type will cause overheads because of conversion to and from word size for operations. Try to keep the data types same for the all the variables used in an arithmetic operations, this will reduce the cycle times of converting one data type to another.

int add (int a, int b)
{
return a + b;
}
Int add (char a, int b)
{
return a + b;
}
Int add (char a, char b)
{
return a + b;
}

first add function will take lesser execution time, compared to other two.

Minimize RAM utilization in memory constraint HW

Many embedded systems will have limitations on RAM, compared to ROM. Stack, heap and global variables constitutes the RAM storage in C program.  Try to optimize the usage of global variable, stack and heap.

To reduce global variable, take a look at variables which are only used as read-only/constant in equations, and use “const” keyword so that they are placed in ROM space. Look out for the strings and any lookup table that will not change in run time, if the case make them constant.

Local variables should be used mostly, since they are stored in registers. This will improve the execution speed, however if too many local variables are declared they will be stored in stack region.

Align data to optimize memory usage

Access to data is very efficient if it’s aligned on the word boundaries/native memory alignment. C compiler will pad variables/structures to stick to the memory alignment, but sometimes this will cost the execution in terms of memory and execution.

Rearranging elements in structure and data arranging can increase the efficiency.

2

Function optimizations

Always write the function to done one specific thing, and keep it very simple and small. Restrict the function arguments to less number, if the number increases then the arguments are pushed to stack and there by costing additional execution cycles to fetch the same. If the arguments re limited, chances are that they are placed in registers and hence increasing the execution speed.

int fun(int a, int b, int c, int d)
{
return a+b+c+d;
}

int fun1(int a, int b, int c, int d, int e, int f)
{
retrun a+b+c+d+e+f;
}

If the register number is only 4, then the fun will be more efficient in terms of execution because all the arguments will be stored in register. But in case of fun1 two arguments will be stored in stack and hence costing additional execution cycles.

fun(int a, int b, int c, int d);

struct
{
int a;
int b;
int c;
int d;
}argument;
fun1(&argument);

fun1 is more efficient compared to fun, because the arguments are passed as reference and also packed together.

If the function is not returning anything, declare it as void because the return value gets stored in register there by costing storage space.

Think of using inline function if the body of function is only 3-4 lines, this will avoid the overhead of function call.

more techniques will follow shortly…..

Advertisements


Categories: programming

Tags: , , , ,

%d bloggers like this: