views:

316

answers:

8

Here is a little test:

function inc(n:integer):integer;
begin
  n := n+1;
  result := n;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  start,i,n:integer;
begin
  n := 0;
  start := getTickCount;
  for i := 0 to 10000000 do begin
    inc(n);//calling inc function takes 73 ms
    //n := n+1; writing it directly takes 16 ms
  end;
  showMessage(inttostr(getTickCount-start));
end;
+13  A: 

Yes, calling a function introduces an overhead. Before calling the function it's necessary to save the current state - which instruction was planned to execute next - and also to copy the function parameters. This requires extra work and extra time.

That's where inlining is helpful. If the compiler supports that it can just injsct the function code directly at the call site and avoid the overhead. With good optimization of surrounding code it can even decrease amount of generated code.

This doesn't mean you need to avoid functions. In most cases the function body executes much longer that the time needed to organize the call. Only in quite rare cases the overhead is worth optimizing. This should never be done without the help of the profiler - otherwise you waste time and most likely just get a lot of unmaintainable code.

sharptooth
The best quote I've read on SO that applies goes like *Avoiding functions to increase performance is like getting a haircut to loose weight.*
Lieven
To add to sharptooth's point: don't worry about avoiding functions. You can make a function inline by declaring it inline, but this is rarely needed: usually, the compiler decides to make functions inline if necessary.
Martijn
Actually in some cases, adding a call **CAN** reduce the global overhead. (see the link in my answer)
François
+4  A: 

Calling a function (whichever language you're working with) generally involves doing a bit more things, like saving some context, pushing parameters to some kind of stack, calling the function itself, reading the parameters, and then pushing the result back somewhere, returning from the function, extracting the return value, ...

So, of course, calling functions generally means having some overhead.

But the main point of functions is re-using some parts of code : maybe it will take a few micro-seconds more at execution, but if you only have to write some code once, instead of 10 (or more) times, there is a huge gain ; and that code will be much easier to maintain, which is really important in the long term.

After, you might want not using functions for some really small parts of code like the one you provided as an example (well, except if the language you're using provides some kind of inlining thing -- it's the case for C, if I remember correctly ; not sure about delphi, though) : the overhead of calling the function will be important, compared to the number of lines of code the function will save you from writing (here : none ! On the contrary ^^ ).
But for bigger parts of code, the overhead will me much smaller, compared to the time taken to execute the bunch of code the function contains...

Pascal MARTIN
Inlining isn't language-dependent, it's compiler-dependent. And yes, Delphi compiler does it.
Pavel Minaev
Delphi compiler added the feature in D2006
Gerry
The modifier in D2005 already afaik. But IIRC the number of cases that it could inline were rarer. FPC supports it since the 2.0 series (2005)
Marco van de Voort
A: 

As others before me said. Yes it does. Every line of code you write does. Functions need to store current states of registers etc... before they can execute and restore it afterwards.

But the overhead is so minimal that optimizing that means nothing. It is more important to have a redable well structured code. Almost always. There may be rare cases when every nanosecond is important but I cannot imagine one right now.

Look here for general guidelines about performance in delphi programs:

http://effovex.com/OptimalCode/opguide.htm

Runner
No, lines of code do not always proportionally translate into performance penalties. In the case of functions in particular, there's inlining.
Pavel Minaev
Ok, true, I was not specific enough :) But it is the general rule.
Runner
A: 

hi,

just want to add some comments specific to Delphi:

  • I think i remember than getTickCount() got a minimal resolution a bit hight to do this kind of test. (+/- 10-15ms). You could use QueryPerformanceCounter() for a better result.

  • for small function called a lot of time (inside process loop, data convertion, ...) use INLINE (search the help)

  • but to know for real what a funciton take and if you should do something about it, use a profiler !! I use http://www.prodelphi.de/, it's pretty simple, very usefull and the price is very correct compare to other profiler (ie: +/-50€ instead of 500€)

  • In delphi, they is the inc() function. It's faster than "n := n+1". ( because inc() is not really a function, it is replaced by the compiler by asm. ie: they is no source code for the funcion inc() ).

Loda
The compiler can optimize `n := n + 1` just fine - it's not really any slower than `inc`.
Pavel Minaev
I confesse that I did not check the asm code, but the doc recommand to use inc() insteed of n:= n+1 for inside loop and other very used code.maybe they improve the compiler since then...
Loda
+5  A: 

Premature optimization is the root of all evil...
Write correct and maintainable code using the known features (here the built-in pseudo(magic) procedure inc), benchmark it and refactor where it's needed for performance reason (if any).

I bet that in 99.9% of the cases, avoiding calling a function or procedure is not the solution.

Here is an example where adding a call to a procedure actually IS the optimization.

François
"conversely, when designing software at a system level, performance issues should always be considered from the beginning"See: http://stackoverflow.com/questions/445648/the-great-optimization-rift/445719#445719
lkessler
+1  A: 

Only optimize when there is a bottleneck.

Your current code is perfectly fine for about 99.9% of the cases.

If it gets slow, use a profiler to point you at the bottleneck. When the bottleneck appears to be in the inc function, then you can always inline your function by marking it with the 'inline' directive.

I totally agree with Francois on this one.

Jeroen Pluimers
A: 

All good comments.

Functions are supposed to be useful, that's why they're in the language. The assumption is that if they have a nominal cost, you are willing to pay that to get the utility they provide.

Here's the real problem with functions, no matter who writes them, but especially if somebody other than you wrote them.

They have an implied contract for what they're supposed to do, but they have no contract for how long they should take.

Usually the person who writes the function thinks "This function does something valuable, so the person who calls it will respect that, and use it sparingly."

Then the person who calls it thinks "This function does so much in only a single call that I can make my code really clean and powerful by calling it lots of times."

Now, with multiple layers of abstraction, this effect acts like compound interest.

So, the real performance problem with functions is not the cost of calls, it is the psychology of programmers, leading to exponential slowdown.

Fortunately, experience in performance tuning can ameliorate this problem.

Mike Dunlavey
+1  A: 

One of the most expensive parts of a function call is the returning of the result.

If you did want to keep your program modular, but wanted to save a bit of time, change your function to a procedure and use a var parameter to retrieve the result.

So for your example:

procedure inc(var n:integer);
begin  
  n := n+1;  
end;

should be considerably faster than using your inc function.


Also, in the loop in your example, you have the statement:

inc(n)

but this will not update the value of n. The loop will finish and n will have the value of 0. What you need instead is:

n := inc(n);


For your timings, do you have optimization on? If you do, then it may not be timing what you thing it is. The value of n is not used by the program and may be optimized right out of it.

To make sure that n is used for the timings, you can simply display the value of n in your showMessage line.


Finally, inc is a built in procedure. It is not good practice to use the same function name as that of a built in procedure as it can cause doubts as to which procedure is being executed - yours or the built in one.

Change your function's name to myinc, and then do a third test with the built in inc procedure itself, to see if it is faster than n := n + 1;

lkessler