这三点都涉及相同的"空函数"问题:
重要编辑调用空虚函数是一样的吗?
编辑 SO基本上你都说大多数情况下编译器会优化它.
但现在我很好奇,因为这仍然适用于这个问题.如果有这样的情况怎么办?在编译时不知道何时调用空函数?它会立即进入堆栈然后退出吗?
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;
}
Run Code Online (Sandbox Code Playgroud)
Sam*_*all 10
虽然它无疑会被任何体面的编译器完全优化,但这是一个比较.
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);
}
Run Code Online (Sandbox Code Playgroud)
在使用VC++ 11的调试模式下(如果有任何优化,它几乎没有)这里是调用的样子:
EmptyFunction();
call ?EmptyFunction@@YAXXZ ; EmptyFunction
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
EmptyFunctionRet();
call ?EmptyFunctionRet@@YAHXZ ; EmptyFunctionRet
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
int返回函数如下所示:
int EmptyFunctionRet() 和 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
Run Code Online (Sandbox Code Playgroud)
没有返回值的函数是相同的,除了它们没有xor eax, eax.
在发布模式下, VC++ 11不会打扰调用任何函数.但它确实为它们生成了定义.int返回函数如下所示:
int EmptyFunctionRet() 和 int EmptyFunctionWithArgsRet()
xor eax, eax
ret 0
Run Code Online (Sandbox Code Playgroud)
而非返回功能再次删除xor eax, eax.
您可以很容易地看到非优化函数非常臃肿,并且在多次循环中运行它们绝对会花费时间,而循环本身将在启用优化的情况下被删除.那么你自己是一个有利的人,优化发布.
你不应该担心这一点,因为它违反了"不要优化".如果您正在使用一个不起眼的平台(如27位MCU),并且您的编译器很垃圾,并且您的性能需要改进,那么您将分析您的代码以查看您需要改进的位置(它可能不是空函数调用) .就目前而言,这实际上只是一个练习,表明我们应该让编译器做多少工作.
编辑您的编辑:
有趣的是,虚函数是优化器的一个已知问题,因为它们在确定指针的实际位置时会遇到极大的麻烦.一些人为的例子可能会消除调用,但是如果通过指针进行调用,它可能不会被删除.
在VC++ 11发布模式下编译以下内容:
class Object {
public:
virtual void EmptyVirtual() {}
};
int main() {
Object * obj = new Object();
obj->EmptyVirtual();
}
Run Code Online (Sandbox Code Playgroud)
在设置代码中产生以下代码段:
mov DWORD PTR [ecx], OFFSET ??_7Object@@6B@
mov eax, DWORD PTR [ecx]
call DWORD PTR [eax]
Run Code Online (Sandbox Code Playgroud)
哪个是对虚函数的调用.
调用空函数时浪费了多少处理时间?
如果你可以使编译器保持空函数,则调用函数的开销和返回值的开销.调用的开销取决于参数的数量以及它们的传递方式.
实际开销取决于处理器以及编译器如何生成代码.
例如,处理器可能有足够的寄存器来包含每个参数,或者编译器可能必须在堆栈上推送参数,并在函数内的堆栈上访问它们.
Edit1:
处理器喜欢处理顺序数据指令.分支,跳转或函数调用强制处理器更改其指令指针,并可能重新加载其指令缓存.由于这些操作不处理数据指令,因此浪费时间并降低程序的性能.这些分支的性能下降仅在高性能程序(要处理的大量数据)或需要满足关键期限的程序(例如通过打印机移动纸张以避免卡纸)中才会显着.
调用100,甚至1000个空函数会产生巨大影响吗?
取决于您对"影响"的定义.根据台式机处理器的当前速度,可以在不到一秒的时间内完成100或1000次重复(更像是小于1毫秒).
您将花费更多时间来编译(翻译和链接)这些功能.
如果这些空函数需要参数怎么办?
就像我在顶部说的那样,取决于编译器和处理器.
调用空虚函数是一样的吗?
虚函数调用可能需要一个或两个以上的处理器指令; 取决于处理器.
个人资料不要假设
分析是你的朋友.分析将告诉您程序中每个函数花费了多少时间.
不要
微观优化您的顾虑被称为微优化.您可能正在优化或担心只能执行20%或更少时间的代码.或者它等待外部过程,如鼠标单击或文件I/O卡住.
抛开性能问题,关注程序的正确性和稳健性.无论运行速度有多快或多慢,运行不正确的程序都会很糟糕.无论崩溃多久,一个易于破解或崩溃的程序都毫无价值.
当用户抱怨或您的程序缺少关键的实时截止日期(例如从流输入源丢失数据)时,只担心性能.
小智 5
试试g++-4.8 -O2 -S -o test.asm main.cpp && cat test.asm.你会看到它在-O2那里g++优化它.
例如,使用以下代码:
void empty_function(int a, int b, int c) { }
int main() {
int a = 42, b = a, c = a;
empty_function(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)
我没有在程序集输出中看到它被调用.
我按照pmr的建议做了以下的练习:
Object.h
#ifndef _OBJECT_H_
#define _OBJECT_H_
class Object {
public:
Object() { }
void empty_function();
};
#endif
Run Code Online (Sandbox Code Playgroud)
Object.cpp
#include "Object.h"
void Object::empty_function() { }
Run Code Online (Sandbox Code Playgroud)
main.cpp
#include "Object.h"
int main() {
Object object;
object.empty_function();
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,它确实调用了空函数:
call __main
leaq 47(%rsp), %rcx
call _ZN6Object14empty_functionEv
xorl %eax, %eax
addq $56, %rsp
ret
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4893 次 |
| 最近记录: |