2

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;
hasen
  • 161,647
  • 65
  • 194
  • 231
isa
  • 1,055
  • 16
  • 26
  • 1
    Your benchmark is a bit unfair for the function. Try this to reduce the overhead: function inc(var n:integer); begin Result := n + 1; end; – Tihauan Aug 13 '09 at 07:05

8 Answers8

14

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
  • 167,383
  • 100
  • 513
  • 979
  • 22
    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 Keersmaekers Aug 13 '09 at 06:08
  • 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 Aug 13 '09 at 07:00
  • Actually in some cases, adding a call **CAN** reduce the global overhead. (see the link in my answer) – Francesca Aug 13 '09 at 16:57
5

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
  • 395,085
  • 80
  • 655
  • 663
5

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.

Francesca
  • 21,452
  • 4
  • 49
  • 90
  • "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 Aug 14 '09 at 00:42
1

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.

Jim McKeeth
  • 38,225
  • 23
  • 120
  • 194
Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
1

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
  • 19,819
  • 36
  • 132
  • 203
0

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
  • 6,073
  • 26
  • 38
  • No, lines of code do not always proportionally translate into performance penalties. In the case of functions in particular, there's inlining. – Pavel Minaev Aug 13 '09 at 06:41
  • Ok, true, I was not specific enough :) But it is the general rule. – Runner Aug 13 '09 at 07:44
0

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
  • 1,970
  • 2
  • 20
  • 40
  • 2
    The compiler can optimize `n := n + 1` just fine - it's not really any slower than `inc`. – Pavel Minaev Aug 13 '09 at 08:17
  • 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 Aug 18 '09 at 08:25
0

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.

Community
  • 1
  • 1
Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135