Best Practices for Writing Efficient Code
Writing Efficient Code | Best Practices for Writing Efficient Code | Efficient Code Example
- Use pointer arithmetic where ever possible, especially in iterative situations.
- Pay attention to the size of the data that you are using. Counting to 10 using an I32U can be inefficient and wasteful of RAM. This also applies to subscripts. Using a 32 bit subscript could take considerably longer to execute than using an 8 bit subscript.
- By ANSI definition all values used in a calculation are promoted to the size of the largest value. For example:
- Where ever possible let the compiler do your math at compile time not run time. For example if you have an array of structures at a specific address and you want the ending address of this array use the following:
- Do repeated identical calculations only once and store the results in statics or globals.
- Re-use temporary variables. This saves memory and may allow for better register re-use.
- Depending on the compiler, putting lengthy loops (large number of iterations) in a separate function may optimize better. This is due to how the compiler determines what to put in registers. A small tight loop in a large function may not optimize as well as the same loop it in a function by itself (not counting the overhead of the function call itself).
- Pass as few parameters as possible to a function. Each parameter passed takes time to put on the stack and read from the stack.
- Pass non-atomic data types by pointer rather then putting them on the stack. This takes less time then copying them onto the stack and also saves on stack space.
- Avoid excessive function call nesting as each function call requires stacking and unstacking of parameters as well as the actual function call.
- In extremely time tight situations consider in-lining code to save the over head of the function call at the expense of code size.
- If you are using many writes to multiple bits in a bit fielded structure in a single function, consider using a byte (or word) wide write instead. Though this is makes the code a little less obvious it reduces the instructions executed. For example replacing 8 one bit bit field writes for a single byte write saves 7 write instructions and possibly more depending on the microprocessor.
- On some microprocessors the RAM is faster than the FLASH. On these systems it might be necessary to use initialized data rather than const data and to possibly put extremely time tight code in RAM.
- Use table lookups where possible for complex math. This can save a significant amount of processing time at the expense of RAM.
- Keep functions as small as possible. They tend to optimize better.
- If at all possible avoid using sprintf and scanf or any of its derivatives. These are generally considered the largest and slowest of the standard library functions. Use to itoa()/atoi() family of functions to convert from hex/decimal to ASCII and vice versa.
- Use memset() and memcpy() for contiguous memory writes and copies. Many compilers have these functions written in assembler and hand optimized. The same is true for strcpy() and strncpy().
- Take advantage of the microprocessor’s register size when copying data. Doing 32 bit reads and writes on a 32 bit processor with a 32 bit data but is much more efficient then doing 8 bit writes. If the 32 bit microprocessor is using a 16 bit bus, however, it might be more efficient to do 16 bit reads and writes.
- By the ANSI standard the register keyword is only a suggestion to the compiler and behaves differently for each compiler. On the PIC C30 compiler a variable with the register keyword is permanently locked into a register and that register is no longer available to use for anything else.
- Use shifts instead of divides or multiples where ever possible.
- Use i32uValue >>= 6; rather than i32uValue /= 64;
i8uResult = i8uValue2 * i32uValue1 / i16uValue2 + 1;
All of the values in this statement will be promoted to I32U because that is the largest value used. This assignment would cause three 32 bit operations to be performed to yield an 8 bit result.
Note: When constants are used (either explicitly or in #defines) their data size is the same as the data size of int (unless explicitly specified in the constant or with a type cast).
#define END_ADDRESS_OF_ARRAY (ARRAY_BASE + sizeof(ST_STRUCTURE) * ARRAY_ELEMENTS)
Writing Efficient Code | Best Practices for Writing Efficient Code | Efficient Code Example





