Author Topic: Small function overhead  (Read 10696 times)

0 Members and 1 Guest are viewing this topic.

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Small function overhead
« on: February 17, 2013, 12:54:30 am »
I've been reading Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin, and have been wondering about the principles for breaking down functions in the context of small embedded systems coded in C++.

He talks about breaking down functions to very small sizes, which makes a lot of sense as he describes it, but the book is generally set in the context of the Java programmer.

I've been trying to work out if splitting a function into multiple functions, that only get called in the one place, has a performance penalty?  Or will the GNU G++ compiler recognise that the function is only called in the one location, and therefore directly insert it as part of the main function?

e.g.  If I had a function
Code: [Select]
int someFunction(int input)
{
int something;
// Say 20 lines of code
return something;
}

And I broke it into multiple functions which are only ever called by the first function within the same class:
Code: [Select]
int someFunction(int input)
{
int something;
something = aBrokenOutFuction1(input);
something = aBrokenOutFuction2(something);
....
return something;
}

int aBrokenOutFunction1(int input)
{
// Do something
return result;
}

....

Will I wear a performance penalty of calling a each of these functions and passing in the variables to them? :-//
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: Small function overhead
« Reply #1 on: February 17, 2013, 01:49:33 am »
I don't remember for sure whether it will by default - and the behavior may differ with embedded compilers - but if you declare the function "inline" it should be inserted into the parent function directly. Declare it "static inline" to also omit the function from the function tables, which may save some space depending on the binary architecture.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: Small function overhead
« Reply #2 on: February 17, 2013, 02:54:45 am »
The GNU compiler is pretty smart when it comes to code optimization so it will likely do the right thing.  I've seen it inline functions that weren't even declared as inline.

The best way to tell what's going on is to have the compiler generate an assembly listing when you build.  You can then peek at this file to see what the compiler has done.
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4192
  • Country: us
Re: Small function overhead
« Reply #3 on: February 17, 2013, 05:51:15 am »
Quote
I've seen it inline functions that weren't even declared as inline.
Sometimes bloating the code in the process.  Here's a case where stopping this behavior reduced program size by about 30%: http://code.google.com/p/arduino/issues/detail?id=822
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: Small function overhead
« Reply #4 on: February 17, 2013, 05:55:37 am »
An important consideration there would be whether they are compiling it by default with the -Os (optimize for size) flag. If so, it is idiotic for GCC to inline those functions. If not, it is perfectly acceptable - GCC's default options are more targeted at PCs, where a few extra kB are nothing.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11519
  • Country: my
  • reassessing directives...
Re: Small function overhead
« Reply #5 on: February 17, 2013, 06:07:18 am »
Quote
He talks about breaking down functions to very small sizes
maybe he's talking about code "manage'ability", but as general rule... breaking down into smaller function only for "reusability" purpose. sometime many smaller functions is not managable at all. extra line break and decent commenting in one single "function body" is much more sensible. trying to chase compiler specific optimization is not really a wise move from "SW eng" point of view. my 2cnts, YMMV.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #6 on: February 17, 2013, 06:40:27 am »
I tried it out using a clean project and Sourcery ARM G++ EABI compiler.


With -Os it doesn't want to inline anything unless the inline keyword is used.  Even if it results in larger binaries and slower execution.


With -O3 it automatically inlines all the very small functions I tried.
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #7 on: February 17, 2013, 06:55:40 am »
Quote
He talks about breaking down functions to very small sizes
maybe he's talking about code "manage'ability", but as general rule... breaking down into smaller function only for "reusability" purpose. sometime many smaller functions is not managable at all. extra line break and decent commenting in one single "function body" is much more sensible. trying to chase compiler specific optimization is not really a wise move from "SW eng" point of view. my 2cnts, YMMV.


Well he talks about a lot of stuff, far too much for me to provide any justice to here, after all he's written an entire book on it.


But I can understand that it's a debatable topic among programmers.  I've seen a number of debates among programmers spouting different languages and coding styles.
 

Offline AlfBaz

  • Super Contributor
  • ***
  • Posts: 2181
  • Country: au
Re: Small function overhead
« Reply #8 on: February 17, 2013, 06:58:36 am »
There must be a break point where function overhead exceeds or comes close to the actual function code. This, the number of calls to it and the type of optimisation called for, you can probably start to see situations where inlining a function might be prudent
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1310
  • Country: 00
Re: Small function overhead
« Reply #9 on: February 17, 2013, 08:04:33 am »
I come from the PC-side of software development where memory and CPU cycles are cheap so I'm in the small-functions-are-'cleaner' camp.  If you've ever spent some time as a software maintenance programmer than you'll know that there really is such a thing as functions that are too large.

Breaking large functions up helps with readability (and thus maintainability) tremendously.  I cringe when I see functions that are more than a couple of hundreds lines long.  100 lines would probably be the biggest I'd like to see.

I only do MCU development as a hobby so I can't really say that it matters one way or the other with regards to code generation.  I focus first on code maintainability and worry about code optimization only if necessary.

There's a saying in software development that was coined by Donald Knuth: Premature optimization is the root of all evil.  That is to say, don't complicate things unnecessarily in the name of optimization.
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Small function overhead
« Reply #10 on: February 17, 2013, 08:34:36 am »
I tried it out using a clean project and Sourcery ARM G++ EABI compiler.

With -Os it doesn't want to inline anything unless the inline keyword is used.  Even if it results in larger binaries and slower execution.
The cost model is constantly tweaked, but GCC will certainly inline when using -Os. But you have to write the code with this in mind. For example, if your function isn't static, the compiler must assume it can be called from some other source file, and therefore include an "outline" copy. If the function body takes less space than a function call, calls in the same source file may still be inlined, but otherwise function a function call will be emitted.

Link-time optimization should be able to handle this, but I've never tried using it.

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13677
  • Country: gb
    • Mike's Electric Stuff
Re: Small function overhead
« Reply #11 on: February 17, 2013, 10:15:27 am »
Quote
There must be a break point where function overhead exceeds or comes close to the actual function code. This, the number of calls to it and the type of optimisation called for, you can probably start to see situations where inlining a function might be prudent
In terms of code size, it should be easy for a linker to figure out if inlining would save space based on number of calls and overhead per call.

Requirements for small embedded systems are somewhat different than for PCs, so GCC is probably not going to make the best decisions unless it can be sufficiently tweaked.
My only experience with GCC is as part of the Microchip PIC24 compiler, which (until I think quite recently) couldn't even be told to omit unused functions, which is pretty pathetic.
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline firewalker

  • Super Contributor
  • ***
  • Posts: 2450
  • Country: gr
Re: Small function overhead
« Reply #12 on: February 17, 2013, 11:40:55 am »
My only experience with GCC is as part of the Microchip PIC24 compiler, which (until I think quite recently) couldn't even be told to omit unused functions, which is pretty pathetic.

Couldn't someone use -ffunction-sections during compiling in addition with -gc-sections during linking?

Alexander.
Become a realist, stay a dreamer.

 

Offline amyk

  • Super Contributor
  • ***
  • Posts: 8230
Re: Small function overhead
« Reply #13 on: February 17, 2013, 12:38:29 pm »
I think what he's saying is the defaults are not sensible and there is no easy way to change them. GCC is a "big system" compiler that just barely works for most of the architectures it supports. Even on the PC where I've used it, the default is to insert tons of useless crap in the binary and it is difficult to figure out how to get rid of it. I can see this being a huge problem for embedded systems work.

Linus has much to say about GCC's inlining here: http://yarchive.net/comp/linux/gcc_inline.html

For embedded systems the default should be "inline-if-smaller".
 

alm

  • Guest
Re: Small function overhead
« Reply #14 on: February 17, 2013, 12:53:22 pm »
I think what he's saying is the defaults are not sensible and there is no easy way to change them. GCC is a "big system" compiler that just barely works for most of the architectures it supports. Even on the PC where I've used it, the default is to insert tons of useless crap in the binary and it is difficult to figure out how to get rid of it. I can see this being a huge problem for embedded systems work.
avr-gcc is quite competitive with the commercial boys like IAR and CV in both size and performance. The commercial boys will usually perform slightly better, but usually something like 20% gain. The Keil (ARM) compiler doesn't appear to be an order of magnitude better than GCC for ARM either, several commercial offerings (eg. Rowley and the several packaged gcc+eclipse distributions like Codered and Attolic) use it with good results.

Linus has much to say about GCC's inlining here: http://yarchive.net/comp/linux/gcc_inline.html
This was written in 2000. I think this was just after the FSF gcc/Cygnus egcs split got resolved. That's quite ancient in gcc history. Did it even support embedded architectures like AVR and ARM back then?

For embedded systems the default should be "inline-if-smaller".
The default for embedded systems is usually -Os, which is quite conservative.
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11519
  • Country: my
  • reassessing directives...
Re: Small function overhead
« Reply #15 on: February 17, 2013, 01:30:21 pm »
He talks about breaking down functions to very small sizes, which makes a lot of sense as he describes it...
i downloaded the book. well, practically he's quite right. you loosely define what is "small" and showing 20 lines of code... 20 lines is "small", its relative to anybody you talking to. ;) but when it comes to hundreds or thousands of lines, then i agree with him, regardless if it is "one function only" or "many small functions combined". let me add to him, he said...
Quote
The first rule of functions is that they should be small.
i say...
Quote
The first rule of code file is that they should be small.
how small? its relative though based on personal taste, for a function, it should be around 20 lines average, but for a file, maybe around few hundreds or few thousands lines? before it can easily out of control. otoh, with commenting and line breaking, one can increase the size meaning of what is "small".

edit: btw, coding thousands of lines in one single function is a very very old practice when no subroutine other than "main" existed. i'm yet to see any such code from the beginning of my programming life. and i never practice it myself, when it gets too big, it will get splitted... naturally. the greatest rule imho is.. use your own flavours and senses to define what is "big" or "small"
« Last Edit: February 17, 2013, 01:46:59 pm by Mechatrommer »
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26682
  • Country: nl
    • NCT Developments
Re: Small function overhead
« Reply #16 on: February 17, 2013, 04:40:53 pm »
Quote
I've seen it inline functions that weren't even declared as inline.
Sometimes bloating the code in the process.  Here's a case where stopping this behavior reduced program size by about 30%: http://code.google.com/p/arduino/issues/detail?id=822
Just read GCC's manual. What -Os and -O3 do is well explained in there. Basically you want to use -Os to get the smallest code with reasonable speed and -O3 for the fastest execution without being bothered with size. Another thing -O3 does is loop unrolling which may be faster.
Sometimes I mix them by compiling some piece with -O3 and other parts with -Os. To reduce function call overhead I usually declare functions inline if they are very small and are called many times from several places. There is a twilight zone between defining a macro or a small function. In the end it is always good not to duplicate code but put it in a macro or a (small) function.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #17 on: February 17, 2013, 09:36:51 pm »
Thanks for all the idea's which I've gone off and tried in ARM G++, and reviewed the assembly listings.


In summary to answer my own question at the start, it's not a wise idea count on G++ to inline functions that you are counting on it doing it for.


Some compiler options result in them being inline. -Os I couldn't get to inline despite trying everything that has been said earlier in this thread.  Whilst I'm sure someone with a better grasp on the specific compiler implementation could come up with how, it just makes sense to explicitly code them inline so it doesn't end up sub-optimal if the code gets reused by someone else without the knowledge that a specific compiler optimization setting must or must not be used.  Or heaven forbid, a completely different compiler is used.


Cheers
 

Offline HackedFridgeMagnet

  • Super Contributor
  • ***
  • Posts: 2027
  • Country: au
Re: Small function overhead
« Reply #18 on: February 17, 2013, 10:06:37 pm »
http://www.amazon.co.uk/Code-Complete-Practical-Handbook-Construction/dp/0735619670 I found Code complete to be a great book, though it is centred around high powered computing and not resource constrained embedded computing. I think it broadly agrees with what TerminalJack505 said.

Could you tell me what Processor you're targeting and what Development tools you are using.
I am looking at moving into Arm Cortex M3-4 and dropping 8 bit processors, and I like to hear other peoples experiences of what works well.
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #19 on: February 17, 2013, 10:09:34 pm »
He talks about breaking down functions to very small sizes, which makes a lot of sense as he describes it...
i downloaded the book. well, practically he's quite right. you loosely define what is "small" and showing 20 lines of code... 20 lines is "small", its relative to anybody you talking to. ;) but when it comes to hundreds or thousands of lines, then i agree with him, regardless if it is "one function only" or "many small functions combined". let me add to him, he said...
Quote
The first rule of functions is that they should be small.
i say...
Quote
The first rule of code file is that they should be small.
how small? its relative though based on personal taste, for a function, it should be around 20 lines average, but for a file, maybe around few hundreds or few thousands lines? before it can easily out of control. otoh, with commenting and line breaking, one can increase the size meaning of what is "small".

edit: btw, coding thousands of lines in one single function is a very very old practice when no subroutine other than "main" existed. i'm yet to see any such code from the beginning of my programming life. and i never practice it myself, when it gets too big, it will get splitted... naturally. the greatest rule imho is.. use your own flavours and senses to define what is "big" or "small"


I'm not in a position where I want to defend one point of view from the other.  I've come from largely coding in C in what would be classed as a fairly "traditional" approach, exactly what you're describing above.  That you just break up functions where reuse is required, and where it just makes sense because they're becoming unwieldy or functionality isn’t related.
In my quest to expand my knowledge and become a better programmer, I want to give his methodology a go.  Whilst he doesn't talk much about specific function sizes (i.e. lines of code), he describes it in terms of a function should do one thing only, which will be described by the title of that function, and will be only at one level of abstraction.
He then goes on to show many examples, generally in the 5-20 line size.
In a later chapter he also talks about size of source files, which he defines a target size of maybe around 200-500 lines.
But, as I was talking about earlier, I know this is contentious issue for programmers.  I've been in a professional software testing course where the presenter was openly bagging Java developers for their very long function names being too similar and ever expanding number of classes.  Then he went on to talk over some Ruby code that I thought was an absolute abomination of a mess.
It’s all relative, but I just wanted to understand from an embedded context where cpu cycles, and more importantly for me with current processors, Flash memory size, is a precious commodity.  Are there going to be any penalties for this style of coding.
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #20 on: February 17, 2013, 10:13:50 pm »
Could you tell me what Processor you're targeting and what Development tools you are using.
I am looking at moving into Arm Cortex M3-4 and dropping 8 bit processors, and I like to hear other peoples experiences of what works well.


I'm now using the STM32 range of ARMs, from their low cost M0 to M4F as the application dictates.


Coding in a roll your own eclipse, Sourcery GCC/G++ and OpenOCD.  Basically because it's free, though not the easiest to setup for the first time (albeit online tutorials can get you over the first humps.)


I don't think you'll find many here that would state that moving from an 8bit range to ARMs is going to be a bad thing.
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11519
  • Country: my
  • reassessing directives...
Re: Small function overhead
« Reply #21 on: February 17, 2013, 11:10:26 pm »
but I just wanted to understand from an embedded context where cpu cycles, and more importantly for me with current processors, Flash memory size, is a precious commodity.  Are there going to be any penalties for this style of coding.
others have covered that pretty well, such as inlining and orphan functions etc, specifically for the gcc. for that you need to dig more ide/compiler specific thing. from my experience, once you do this, you are ready to break any "clean coding practice". this is a matter of "compiler or system specific" which i believe the book wont bother to touch. you maybe lucky with gcc, but not neccesarily with other compiler. the sure fire to get the performance (speed and size) in any compiler is a "one big function". what the compiler do inside, is out of our reach. the easiest way to get what you want is change compiler setting and re-compile and analyze each output. you can change the code to satisfy the compiler and yourself, and re-compile. and you can do this again and again until you get the most perfect output. may as well the today's compilers are intelligent enough? well we need not care anymore, as usual. let me quote page 20... which caught my eye during "compiler" search...
Quote
Programmers create problems for themselves when they write code solely to satisfy a compiler or interpreter
i know its under "Make Meaningful Distinctions" topic, but i believe it also applies, generally, for the wellbeingness of a programmer. and another OT suggestion i've heard somewhere else, if its not enough, get bigger mcu or go for assembly, performance'wise. YMMV.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline HackedFridgeMagnet

  • Super Contributor
  • ***
  • Posts: 2027
  • Country: au
Re: Small function overhead
« Reply #22 on: February 18, 2013, 12:25:33 am »
I like to write code with lots of function calls, the reasons are for "Procedural Abstraction" and also to help with code re-use.

Naming the function and specifing the function definition are the key parts of Procedural Abstraction.
Ideally somebody looking at your header file should be able to call your function from their code without actually looking at the implementation.
ie. the function name should be meaningful, the parameter names should be meaningful and the types appropriate.
In C++ The return value should be the obvious result of the function name, whereas in c the return result is often the error condition.
In tightly constrained systems "Procedural Abstraction" wont always be possible.
I got a lot of these ideas out of the book "Code Complete" and from doing O-O programming. I am sure many embedded developers approach it differently.
 
 

Offline jerry507

  • Regular Contributor
  • *
  • Posts: 247
Re: Small function overhead
« Reply #23 on: February 18, 2013, 02:25:46 am »
With the rise of ARM there is considerably less resource constraint then there used to be. At least for my typical use cases, it's never worth it to try and fit code into a small size, it just takes too long. My development time is typically much smaller than my budget, so I tend to follow the software practices of the PC folks more than the embedded folks. Their development cases more closely match what I usually see.
 

Offline HarvsTopic starter

  • Super Contributor
  • ***
  • Posts: 1202
  • Country: au
Re: Small function overhead
« Reply #24 on: February 18, 2013, 07:37:57 am »
To give a little bit more context, here's the test functions I was using:
Code: [Select]
#include "ledDriver.h"


LedDriver::LedDriver()
{
   enableLedGPIOClock();
   configPort();
   turnOffLeds();
}


//inline
void LedDriver::enableLedGPIOClock() {
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
}


//inline
void LedDriver::configPort() {
   GPIO_InitTypeDef GPIO_init_struct;
   GPIO_init_struct.GPIO_Pin = LED2_pin | LED3_pin;
   GPIO_init_struct.GPIO_Mode = GPIO_Mode_OUT;
   GPIO_init_struct.GPIO_Speed = GPIO_Speed_Level_2;
   GPIO_init_struct.GPIO_OType = GPIO_OType_PP;
   GPIO_init_struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_Init(GPIOC, &GPIO_init_struct);
}


//inline
void LedDriver::turnOffLeds() {
   GPIO_ResetBits(GPIOC, LED2_pin | LED3_pin);
}


Obviously it's not necessarily a reflection of what you'd do in reality, but it served the purposes by breaking down a function I already had.

Everything except the constructor is declared private.

The overhead for each function call by not inlining with -Os was 12 bytes (36 bytes total in this example).  May not sound like a lot, but I think it's significant when it all adds up.[/code]
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf