Skip to main content
gamedev.net
🔒 Locked

Function Calls - Slower Execution?

Started by JoshG Jan 8, 2002 at 2:50 AM 17 replies 900+ views
Original Post
JoshG
JoshG
hello, I read somewhere in a post that Function calls slow the execution of a program because the stack has to allocate memory for the variables that are passed to the function and what not. so my question is, if my function doesn''t have any variables sent to it, ie. void MyFunction(); then does it slow my Execution down? --Josh
MadKeithV
MadKeithV
Yes - the return address still has to be stored on the stack.
It's only funny 'till someone gets hurt.And then it's just hilarious.Unless it's you.
JoshG
JoshG
is there any way to get rid of that?

I suppose another question would be properly... If I were to have pointers to functions that did different things, and were called when cirtain input was obtained, ie, a key was pressed. And there was constantly input being recieved, Would calling all those functions slow down execution drastically? or would it be acceptable?

How would it compare to inlining the whole opperation in ONE function? But it wouldn''t be as dynamic

--Josh
BaShildy
BaShildy
Everything comes at a price, and calling a function does eat up cycles. You got the right idea of limiting parameters though, that''s a way to prevent abusing the stack. You could also pass a pointer which is a very cheap operation. You will almost never come to the point where 1 function call eats up the ''last'' cycle that your program needs to run smoothly, so don''t worry about speed when it comes to modularizing code.

Theres a guy on this site who has the sig:
Make it work
Make it right
Make it fast

Get everything working smoothly first and don''t worry about speed. If you need speed after that then you can optimize. I always follow the rule that: drawing code is fast and mean, and the rest of my project is lean and clean. I have COMPLETED projects because I followed that rule. Don''t worry about speed on non-drawing code, make it as readable as possible.

If you cannot program, or you cannot draw, you are not a "game designer".
Kevin "BaShildy" KingGame Programmer: DigiPenwww.mpogd.com
Arild Fines
Arild Fines
quote:
Original post by JoshG
Function calls slow the execution of a program because the stack has to allocate memory for the variables that are passed to the function and what not.


Moving the stack pointer down a number of bytes is hardly an expensive operation.



Fantastic doctrines (like Christianity or Islam or Marxism or Microsoft-bashing) require unanimity of belief. One dissenter casts doubt on the creed of millions. Thus the fear and hate; thus the torture chamber, the iron stake, the gallows, the labor camp, the psychiatric ward - Edward Abbey
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
CaptainJester
CaptainJester
I shortened it. I found out a couple of weeks after I registered that the make it right part was not supposed to be there.

I think there are some articles on here that say something like your program spends 95% of its time in 10% of your code, so you may tweek an area to get an extra cycle, but then if the program spends no time in there, you just wasted your time. The first thing you need to do is finish the program, then if you notice that it is slower than you would like, run it through a profiler. The profiler can tell a lot better where to optimize the code.

---
Make it work.
Make it fast.

Edited by - CaptainJester on January 8, 2002 8:39:47 AM
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
SteveC
SteveC
There''s generally no real point in optimizing until AFTER you have profiled your app.

Certainly experienced programmers have a ''feel'' for what things need to be fast in their domain - the thing is its likely not worth spending a huge amount of time making it fast up front.

setting up an architecture to optimize it later is, however, worth it - such as function pointers to critical routines which could later be optimized in hand-rolled assembly.

but without profiling you don''t know if you will actually be making a substantial improvement - even if you improve some code''s speed by making it 5 times faster, it won''t be significant if its only used 1% of the time.

As far as speed of parameters to functions, the only thing to really be on the lookout for is making sure that structures and classes are NOT passed by value
eldee
eldee
another way to speed things up would be to use inline functions for
functions you call often.
it''s recommended that you only use it on smaller functions, since
an inline function essentially tells the compiler to copy the function''s
code to the place where it was executed (thus making your exe larger).


-eldee
;another space monkey;
JoshG
JoshG
Thanks,
There has been a great lot of interesting points made!
Thank you for enlightening me. I''ll go and finish my code now hehe

When you say "profile" my code, how does that happen?

--Josh
hemihua
hemihua
If your''e programming in C, there will be a 1 parameter stack overhead (return address), if you''re programming in C++ and the function is a method of a class, there will be a 2 parameter stack overhead (return address + object). You can see this when programming in directx using C - u have to use
dxobject->lpVtbl->function(dxobect, .....);
Really, it makes f*** all difference =)
trzy
trzy
There is also another nasty stack-related thing that functions tend to drag in to the fray: Frame pointer overhead.

Take the following function, for example:

void DoAbsolutelyNothing()
{
}

The compiler turns this into:

DoAbsolutelyNothing:
push ebp
mov ebp, esp
mov esp, ebp
pop ebp
ret

In GCC, the only way to get rid of this is the -fomit-frame-pointer option, not even the -O options will optimize this away.

The reason for it is to allow for constant offsets to function arguments (through EBP) regardless of how much junk has been pushed on to the stack as the function is executed. It also provides for a quick escape mechanism, because you don''t have to worry about keeping track of what to pop off the stack when you want to exit, just move EBP back into ESP, pop EBP off the stack, and you''re done!

I suspect for complex functions which manipulate the stack a lot, this can be a benefit, but for most functions, it''s not necessary. I haven''t really studied how it affects complex functions, so I don''t know for sure.

Maintaning a frame pointer in EBP is also helpful to debuggers, which can use the data in EBP to figure out which function called the one that crashed the program, for instance.

I usually use -fomit-frame-pointer to get rid of it, and when writing in assembly, I never use the frame pointer. This is neat, because it not only gets rid of the overhead of managing it, but also allows me to use EBP as a general-purpose register if I''m squeezed to the point where I need it. The negative consequences are that I have to carefully manage argument access (not hard at all), and if my assembly function crashes, the debugger might not be able to tell me where the crash occured. But these are risks I''m well aware of, and when I use assembly language, I''ve already weighed everything into consideration and realize that the pay-off will be worth the price of the minor annoyances.

A lot of assembly books use a frame pointer, like so:

;
; Add
;
; This procedure adds the first argument on the stack with the second and returns the result in EAX.
;
Add:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; first argument
add eax, [ebp+12] ; plus second argument
pop ebp
ret

Whereas, the better way (IMHO) would be:

Add:
mov eax, [esp+4]
add eax, [esp+8]
ret



---
Bart
----Bart
LordKronos
LordKronos
A few points:

First of all, whenever trying to optimize performance:
1)As other said, only bother to optimize what a profiler says you need to optimize. Otherwise you will most like optimize unnecessarily.

2)Always make sure you profile on an optimized build. Not sure about other compilers, but with VC++, when you compile in debug build, you get unoptimized code. Actually, the code is worse than unoptimized in that aspect that many functions in the standard library actually have a bunch of error checking code that is removed in release build.


hemihua: No, the "this" pointer does not get passed on the stack, it gets passed in a register (ecx, if I recall). At least this is how VC++ does it. I suppose a different compiler might do it differently, but then again, if that were the case then you could never call a class function in a DLL compiled with a different compiler. My experience is limited to VC++, so I cant authoritatively state any more than that. Of couse, placing this in a register implies that the contents of that register may need to be preserved (either by pushing it on the stack, or by reloading it from its memory location after return), but that is normally assumed when making a function call anyway (assume the function call will trash all registers and save those you will need after return).


trzy: In VC++, you can eliminate this prolog/epilog code by prefixing the function with "__declspec( naked )".

Ron FrazierKronos Softwarewww.kronos-software.comMiko & Molly - Taking Puzzle Games to A Whole New Dimension
matrix2113
matrix2113
although this has nothing to do with functions, you can use the ''register'' keyword to put variables that are used often into their own register in order to speed things up
note: Many compilers, including VC++, will almost always ignore the register keyword, because the compiler can do a better job of optimazation.

"I''ve learned something today: It doesn''t matter if you''re white, or if you''re black...the only color that REALLY matters is green"
-Peter Griffin
"I've learned something today: It doesn't matter if you're white, or if you're black...the only color that really matters is green"-Peter Griffin
Shannon Barber
Shannon Barber
The biggest problem with function calls and optimizations, is that you can''t optimize code across a function call. When you call a function, you establish a contract about what must happen. It is for this reason that optimizing compilers (as useful as they are) cannot compete with a person. You''re free to change the _structure of the program to make it faster, whereas an optimizer must make certain that whatever the code did before is still accomplished post-optimization.

Eliminating function calls lets the optimizer do a more complete job. In most of the code it won''t make much difference - in some of the code it makes a huge difference.
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
DrPizza
DrPizza
quote:
Original post by Magmai Kai Holmlor
The biggest problem with function calls and optimizations, is that you can''t optimize code across a function call. When you call a function, you establish a contract about what must happen. It is for this reason that optimizing compilers (as useful as they are) cannot compete with a person. You''re free to change the _structure of the program to make it faster, whereas an optimizer must make certain that whatever the code did before is still accomplished post-optimization.

This is why you allow inlining of anything appropriate. It is rare that the size will matter a damn.

Also, different calling conventions help. If you''re not exporting the code as a library or anything, passing arguments in registers (particularly with only one or two arguments) is one way of reducing the function call overhead. In VC++, __fastcall will do this for you. One has to be wary of mixing calling conventions; if one place is expecting one convention and another another, you''ll corrupt your stack or worse.

char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;} /*** drpizza@battleaxe.net ***/
Halloween
Halloween
Indeed, compiler decides himself if to inline function.
But there is Microsoft-specific __forceinline to make it inlined for sure.
hemihua
hemihua
LordKronos: Yeah, i guess that would be an obvious optimization for the compiler to do. AFAIK windows requires registers EDI, ESI, EBP and EBX to be preserved.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.