embedded software boot camp

Efficient C Tip #11 – Avoid passing parameters by using more small functions

Saturday, February 6th, 2010 by Nigel Jones

This is the eleventh in a series of tips on writing efficient C for embedded systems. Today’s topic will, I suspect, be slightly controversial. This post is based upon two basic observations:

  1. Passing parameters to functions is costly.
  2. Conditional branch instructions can be very costly on CPUs that have instruction caches (even with branch prediction).

I don’t think that too many people will disagree with me on the above. Despite this I too often see a style of coding that incurs these costs unnecessarily. I think it’s best illustrated by a (real world) example. The issue is one that will be familiar to most of you.  An embedded system contains a number of discrete LEDs (say 3), and the requirement is to write some code to allow higher level code to either turn on, turn off, or toggle a particular LED. The way I often see this coded is as follows:

typedef enum
{
 LED1, LED2, LED3
} LED_NO;
typedef enum
{
 LED_OFF, LED_ON, LED_TOGGLE
} LED_ACTION;
void led(LED_NO led_no, LED_ACTION led_action)
{
 switch (led_no)
 {
  case LED1:
   switch (led_action)
   {
    case LED_OFF:
     PORTB_PORTB0 = 0;
     break;
    case LED_ON:
     PORTB_PORTB0 = 1;
     break;
    case LED_TOGGLE:
     PORTB_PORTB0 ^= 1;
     break;
    default:
     break;
   }
  break;
 case LED2:
  ...
}

So what’s wrong with this you ask? Well in a nutshell the parameters passed to the function are used strictly to control the order of execution. There is no code common to any pair or group of parameters. When faced with a situation such as this, I instead implement the code as a large number of very small functions. For example:

void led1_Off(void)
{
 PORTB_PORTB1 = 0;
}
void led1_On(void)
{
 PORTB_PORTB1 = 1;
}
void led1_Toggle(void)
{
 PORTB_PORTB1 ^= 1;
}
...

Let’s compare the two approaches.

Efficiency

This blog posting is supposedly about efficiency, so let’s start with the results. I coded these two approaches up together with a main() function that exercised all 9 possible combination’s. I then turned full speed optimization on and looked at the results for an AVR processor.

Single function approach: 78 bytes for main(), 94 bytes for the LED code. Execution time 208 cycles.

Multiple function approach: 42 bytes for main(), 54 bytes for the LED code. Execution time 96 cycles.

Clearly my approach is significantly more efficient.

Usability

By usability I’m referring to the case where someone else needs to use your code. They know they need to say toggle LED2 so they hunt around and find the file led.h. The question is, once they have opened up led.h, how quickly can they determine what they have to do in order to toggle LED2? In the single function case they are presented with just one function (which is a plus), but then they have to locate the enumerations and work out the parameters that need to be passed to the function (which is a minus). In the multiple function case, they have to search through a list of functions looking for the correct one. However once they have found it, it’s very clear what the function does.

For me, I think it is a toss up between the two approaches as to which is more usable.

Maintainability

In this case the multiple function approach is the big winner. To see how this is, consider what happens to the single function case when one adds an LED or adds an action. The single function case just explodes in size, whereas with the multi-function approach one simply adds more very simple functions.

Conclusions

If you buy my analysis then clearly the multi-function approach is superior in both efficiency and maintainability – two areas that are dear to my heart. Now granted this is a fairly extreme example. However in my experience if you look through a reasonable amount of code you will soon discover a function that essentially does one thing or another based upon a function parameter. When you locate such a function you might want to try breaking it into two functions in the manner described here – I think you’ll be pleased with the results.

Next Tip

Previous Tip

****
As the readership of this blog has grown I must say I have been really impressed with the many insightful comments that have been posted. I know I learn a lot from them, and so I suspect, do a lot of the other readers. Thus for those of you that have commented in the past – thank you. For those of you yet to post a comment, I encourage you to take the plunge!

Home

Tags:

18 Responses to “Efficient C Tip #11 – Avoid passing parameters by using more small functions”

  1. Dan says:

    Nigel – good post. I often find myself making the same argument, and often it's an uphill battle.In the first example (parameters passed to a mega-function), what we have is one of my biggest pet-peeves: control coupling. The parameters are really control flags, especially the 2nd parameter (action).Secondly — watch out, I'm going to play the OO card — notice how led1_Off() would probably coded as led1.Off() in an OO language (or maybe LedBank.Off1(), depending on what makes sense).The first example would be as if there was a static (class-wide) "Control" function for the LED class, and the LED "object" and the operation were passed as parameters, such as:LED::Control(&LedObj, TURN_ON);Don't laugh. This happens a lot when organizations first pick up the shiny new C++ hammer & "port" their code!

  2. Gauthier says:

    I wonder how it'd look like without the optimisation.For example, did the compiler inline the small functions in your example?For more complex functionality, there's always the risk of breaking the DRY principle. I suppose that's why people would make a single big function (maybe unconsciously)?Breaking DRY makes maintainability harder… I agree that this does not really apply to simple handling of LEDs, though.I have coded such "big functions" in the past, only they were macros and therefore handled offline by the preprocessor.I have to admit that the preprocessor was a bit more capable than that of C (that was dsp assembly).That way it feels easier to avoid copy-pasting code, yet have the efficiency benefits.Of course there is the memory issue, but the generated code was alway either very short or the macro used very seldom, and you save the branch.I do not think you would agree to apply that to C?What happens if the big function is inlined? I would love to see the optimiser prune off all the unneeded cases in each instance of the inline function. But I am surely only dreaming.

  3. Mans says:

    Gauthier: GCC performs that kind of inlining very well. I don't know about compilers for tiny microcontrollers though.Also consider the (quite likely) case that the control registers for the LEDs are at consecutive addresses. Then setting the state of an LED will look something like led_regs[led_no] = state, and a single function can be written without using a switch statement or similar. Depending on the instruction set this may or may not benefit from inlining, but using lots of separate functions is probably not the best solution.If the control registers are scattered more or less randomly, individual functions are more likely to be advantageous.

  4. Gauthier says:

    @Mans: do you mean inlining of the many small functions, or inlining of the big one and the compiler optimising away the unnecessary cases?

    • David Brown says:

      avr-gcc will do both, inlining small functions and the big function. But it needs a little help. First you have to make sure that the function definition is known at compile-time – either include the function definition in the same module that it is used, declare it “static inline” in a header file, use -fwhole-program, or try out the new LTO options.

      Secondly, while gcc will automatically inline the small functions, it will not automatically inline a big function like this (unless it knows there is only one use of that function in the program). So you have to label it “inline”.

  5. Kepa Díez says:

    I agree on that most of the times it is more code (and speed) efficient to use smaller functions, instead of a single larger function with a parameter.But is the code efficiency the most important aspect in every project? In many projects there are lots of idle CPU cycles. I know that power can be saved putting the MCU in low-consumption mode during the idle periods… but it is also true that with modern designs many times it is not worth saving 5 mW in an appliance with a 1 kW consumption, especially if that means spending more time coding and mantaining SW (with the monetary cost and the increased energy consumption during that time period).Another point is the maintainability; if many of the small functions share some functionality (code), which is frequently the case, it will probably easier to maintain if they are grouped in a single, larger function. And if these functions are not the bottleneck of the embedded system, I see no point over-optimizing them.But I also agree that long functions tend to be troublesome from the mantainability point of view, although there can be "hybrid" approaches to solve this problem to some extent. Personally, I tend favour multiple small functions instead of a single big one, except for the cases where the number of small functions is huge, or when there is a lot of common code shared between the small functions.This is a interesting topic to discuss on, since I think that there are some compromises to be made, depending on the choosen approach.

  6. Nigel Jones says:

    Some nice observations Kepa. While the blog posting is about efficient C, it is always a very interesting discussion as to whether efficient C is contrary to maintainable C.

  7. Sergio Prado says:

    Very good post Nigel!Another good idea is to use some kind of data oriented implementation. For example, you could have:void led(LED_NO led_no, LED_ACTION led_action){ (*led_action)(led_no);}In this case, you would have a structure defining the led number and the port that it is connected, and a group of led functions defined by LED_ACTION. I do not know if it is more efficience than your code, but I think it would be more maintainable, since to include a new led you do not need to create three new functions. It would be just the case of adding a new element in the leds structure.Keep the good work!Sergio PradoSão Paulo/Brazil

  8. david collier says:

    I'm with kepa.The inefficiency of breaking the code by failing to correctly edit one of multiple copoiies of the same code is what I'd be worrying about.Now in practise, one could/wold probably be able to set up a constant array of bit masks, which would allow one to deal with the selection of the target LED by picking out an array entry.I might even go so far as to use something similar yo do the actions – though that might end up looking a bit clever-clever…. but and, or, xor in turn with 3 fields in an array would work fine – though it would need careful commenting.D

  9. Carlos says:

    Suppose we have several leds (say 32) in our system, and we want to turn them ON in a consecutive way (maybe a lighting application?). With the single function approach the code could be:for (i=0; i<32; i++){ led(i, LED_ON); wait();}While with the multiple function approach:led1_On();wait();led2_On();wait();led3_On();wait();…led32_On();wait();I guess someone may have better ways of coding this, but if this is not the case, and we use this code, can it be possible that the overhead introduced by the second approach (due the amount of code aggregated) be at the end more costly than using the single function approach?What about its flexibility?

  10. Frank says:

    @Dan: the OO approach is very nice, I use it too.
    My Duo-LEDs can
    – be turned on or off and toggle
    – can blink
    – and can change its color.
    This leads to member functions
    – C_DuoLED_On(struct C_DuoLED* me), C_DuoLED_Off(struct C_DuoLED* me), C_DuoLED_Toggle(struct C_DuoLED* me)
    – C_DuoLED_BlinkStart(struct C_DuoLED* me), C_DuoLED_BlinkStop(struct C_DuoLED* me)
    – and C_DuoLED_SetColor(struct C_DuoLED* me, eLedColor color).

    This seems to be a lot of functions, but all commands are completely orthogonal, the first parameter is always a kind of “this”-pointer to the instance of the Duo-LED. This instance keeps track of the state of the LED such as the current color, the on/off-state, a link to the SW-timer for blink support and the IO-pins of the CPU where the LED is connected to. In this way the LED always knows about its own state, there’s no need for bookkeeping by clients that simply want to use the LED.

  11. Doug says:

    I guess to me this solution is only about half-way to the most efficient solution. To turn on an LED I define a macro such as “#define LED1_OFF PORTB_PORTB1 = 0;”. Then a call to LED1_OFF is visually descriptive, doesn’t use any stack space for call and return, requires only one line of assembly code and can be changed in one location if the hardware changes (moving LED1 to a different GPIO for instance).

  12. Bernhard Weller says:

    While this may be nice and useful in case of switching on and off LEDs, I doubt you can use this technique for helper functions like converting different dataformats.
    I found myself in the position to renew a part of an electronic which used a PIC, sending some sensor data already calculated and normalized to a value ranging from zero to one in a floating point format. Now I used a MSP430 in this case. Being not really familiar with the PICs I just implemented the feature as anyone would.
    I got myself in trouble on the first sensor readings which had some funny values, and I started wondering what was going on. The problem lies in the floating point format, PIC uses it’s own format and the other side of the communication was expecting a PIC float. The MSP uses the IEEE-754 floating point format.
    So I wrote a function which turned a IEEE-754 floating point number into four bytes (ready for serial communication) representing the PIC-float with the same value.
    Now how would you prevent passing an argument to a function in this case?
    Using a global variable comes to mind, but that’s sacrificing good coding style (no use of global variables where possible).

    I now pass references to the converter-function, this reduced code size by a few bytes (about 20 bytes):
    [code]
    void ConvertFlt32ToPIC(const Flt32 &infloat, uByte &lsbbyte, uByte &midbyte, uByte &msbbyte, uByte &expbyte)
    [/code]
    References as parameters are a C++ construct (if I remember correct) so a C implementation would probably use a const pointer to some type (uByte* const, const Flt32 * const)
    I tried some things with references and it seems like they only reduce code size (I haven’t checked on cycle count) if the variable type is longer than the normal size the processor handles. So in case of a 16-bit MCU passing as reference seemed only beneficial if the parameter is bigger than 16 bit.

  13. I think that both approaches should be considered when designing interface. For example when multiple similar peripherals are used one big function (or separate function for each action) can be usefull. I also prefers when variable passed is USART1/USART2… than dealing with functions with prefixes.

    On the other hand separate function for each object/action are imho good decision when objects are separate and not used ass group/array of objects. This also make it easier to adjust implementation for specific needs (i.e. inverted logic on some LEDs).

    Personally when few clock cycles efficiency is not needed it might be the best way to create class led, objects for led1, led 2… etc – and led* array when iteration through multiple leds is needed.

  14. […] C Tips #7 – Fast loops Efficient C Tip #11 – Avoid passing parameters by using more small functions Efficient C Tip #13 – use the modulus (%) operator with […]

  15. […] C Tips #7 – Fast loops Efficient C Tip #11 – Avoid passing parameters by using more small functions Efficient C Tip #13 – use the modulus (%) operator with […]

  16. vishal says:

    hi nigel
    thank you for interesting post . i want to ask you that, can we do something like this

    suppose i have two led’s ,lets say green and yellow. so as per your suggestion i will make function as follow

    void Led_Green(status_t Led_State);
    void Led_Yellow(status_t Led_State);

    typedef enum {LED_ON, LED_OFF,LED_TOGGLE} status_t;

    void Led_Green(status_t Led_State)
    {
    if(Led_State = = LED_ON)
    {
    // make led high
    }
    if(Led_State = = LED_OFF)
    {
    // make led Low }
    }
    if(Led_State = = LED_TOGGLE)
    {
    // make led high
    }

    }

    i just wanna ask you is it worth or not?