These three points all relate to the same "empty function" question:
Important edit Is calling an empty virtual function the same?
Edit SO basically you are all saying that in most cases the compiler will optimize this out.
But now I'm curious as this still applies to the question. What if there's a situation like so, where it is not known at compile time when an empty function will be called? Will it immediately go to the stack then exit?
class base{
public:
virtual void method() = 0;
};
class derived1: public base{
public:
void method() { }
};
class derived2: public base{
public:
void method(){ std::cout << " done something "; }
};
int main()
{
derived1 D1;
derived2 D2;
base* array[] = { &D1, &D2 };
for( int i = 0; i < 1000; i ++)
{
array[0]->method();// nothing
array[1]->method();// Something
}
return 0;
}
While it will most undoubtedly be optimized out completely by any decent compiler, here's a comparison.
void EmptyFunction() {}
void EmptyFunctionWithArgs(int a, int b, int c, int d, int e) {}
int EmptyFunctionRet() {return 0;}
int EmptyFunctionWithArgsRet(int a, int b, int c, int d, int e) {return 0;}
int main() {
EmptyFunction();
EmptyFunctionWithArgs(0, 1, 2, 3, 4);
EmptyFunctionRet();
EmptyFunctionWithArgsRet(5, 7, 6, 8, 9);
}
In Debug Mode with VC++11 (which has very little if any optimization) here's what the calls look like:
EmptyFunction();
call ?EmptyFunction@@YAXXZ ; EmptyFunction
EmptyFunctionWithArgs(0, 1, 2, 3, 4);
push 4
push 3
push 2
push 1
push 0
call ?EmptyFunctionWithArgs@@YAXHHHHH@Z ; EmptyFunctionWithArgs
add esp, 20 ; 00000014H
EmptyFunctionRet();
call ?EmptyFunctionRet@@YAHXZ ; EmptyFunctionRet
EmptyFunctionWithArgsRet(5, 7, 6, 8, 9);
push 9
push 8
push 6
push 7
push 5
call ?EmptyFunctionWithArgsRet@@YAHHHHHH@Z ; EmptyFunctionWithArgsRet
add esp, 20 ; 00000014H
The int returning functions look like this:
int EmptyFunctionRet()
and int EmptyFunctionWithArgsRet()
push ebp
mov ebp, esp
sub esp, 192 ; 000000c0H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-192]
mov ecx, 48 ; 00000030H
mov eax, -858993460 ; ccccccccH
rep stosd
xor eax, eax
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
The functions with no return value are the same, except they don't have the xor eax, eax
.
In Release Mode VC++11 doesn't bother calling any of the functions. But it does generate definitions for them. The int returning functions look like this:
int EmptyFunctionRet()
and int EmptyFunctionWithArgsRet()
xor eax, eax
ret 0
Whereas the non-returning functions again remove the xor eax, eax
.
You can see easily that the non-optimized functions are extremely bloated, and running them in a loop of however many times would absolutely cost time, whereas the loop itself would be removed with optimizations turned on. So do yourself a favor folks, release with optimizations on.
You shouldn't worry about this, as it violates "Don't Optimize Yet." If you're working with an obscure platform (like a 27-bit MCU), and your compiler is garbage, and your performance needs improvement, then you would profile your code to see where you need improvement (it's likely not empty function calls). As it stands, this is really just an exercise that shows how much we should let the compiler do the heavy lifting.
EDIT FOR YOUR EDIT:
Interestingly, virtual functions are a known problem with optimizers, as they have extreme trouble figuring out where pointers are actually going to go. Some contrived examples might eliminate the call, but if there's a call through a pointer, it's probably not going to be removed.
Compiling the following in VC++11 Release Mode:
class Object {
public:
virtual void EmptyVirtual() {}
};
int main() {
Object * obj = new Object();
obj->EmptyVirtual();
}
Yields the following snippet among the setup code:
mov DWORD PTR [ecx], OFFSET ??_7Object@@6B@
mov eax, DWORD PTR [ecx]
call DWORD PTR [eax]
Which is the call to the virtual function.
How much processing time is wasted when calling an empty function?
If you can make the compiler keep your empty function, there is the overhead of calling the function and the overhead of returning a value. The overhead of calling depends on the number of parameters and how they are passed.
The actual overhead depends on the processor and how the compiler generates the code.
For example, the processor may have enough registers to contain every argument or the compiler may have to push arguments on a stack, and access them on a stack inside the function.
Edit1:
Processors like to process sequential data instructions. Branches, jumps or function calls force the processors to change their instruction pointers and maybe reload their instruction cache. Since these operations are not processing data instructions, they are wasting time and slowing down the performance of your program. The degradation of performance from these branches is only noticeable in high performance programs (lots of data to process) or programs that need to meet critical deadlines (such as moving paper through a printer to avoid paper jams).
Would it make a huge impact to call 100, or even 1000 empty functions?
Depends on your definition of "impact". With the present speed of desktop processors, 100 or 1000 repetitions can occur in less than a second (more like less than 1 millisecond).
You would spend more time compiling (translating and linking) these functions.
What if these empty functions required arguments?
Like I stated at the top, depends on the compiler and processor.
Is calling an empty virtual function the same?
A virtual function call may require one or two more processor instructions; depends on the processor.
Profile don't Assume
Profiling is your friend. Profiling will tell you how much time is spent in each function in your program.
Don't Microoptimize
Your concerns are called micro-optimizations. You could be optimizing or worrying about code that only gets executed 20% or less of the time. Or it is stuck waiting for an external process such as a mouse click or file I/O.
Put performance issues aside and focus on the correctness and robustness of your program. A program that runs incorrectly is bad regardless of how fast or slow it is. A program that can be easily broken or crashes is worthless regardless of how soon until the crash.
Only worry about performance when Users complain or your program is missing critical real-time deadlines (such as missing data from a streaming input source).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With