优化后的虚拟调用成本
cost of virtual calls with optimization
当指向的类型总是相同时,我有一个关于虚拟调用成本的问题:
class Base
{
Base() {};
virtual void Func() = 0;
};
class Derived
: public Base
{
Derived() : Base() {};
void Func() { /* Do something */ };
};
int main()
{
Base* base = new Derived;
for (int i = 0; i < 1000; ++i)
{
base->Func();
}
return 0;
}
编译器会优化这个虚调用吗?
GCC with -O3似乎没有优化虚调用。
https://goo.gl/TwZD6T.L5
movq (%rdx), %rdx
cmpq Derived::Func(), %rdx
je .L3
movq %rbp, %rdi
call *%rdx
subl $1, %ebx
jne .L11
进行函数指针比较,如果不相等,则继续进行间接函数调用。
要优化一个函数的虚拟性是相当困难的。在适当的编译时,您无法知道该虚函数不会产生任何影响。只有在链接的时候你才能知道。
更有问题的是,你甚至可能无法知道,因为你可能动态加载实现了另一个子类的共享库,而这个子类可能会覆盖虚函数。
基本上这样的优化需要相当智能的链接时间优化,可能是相当小的增益。
这取决于你的编译器是否聪明;也许它可以优化,也许不能。它也是一个实现细节——您更关心代码的整体性能,而不是像这样的具体细节。衡量你的性能,并决定是否需要优化,如果是,你的方法。
如果你真的关心这样的代码,你可以选择使用c++ 11关键字final
,并在循环外对类型进行测试,而不仅仅是在循环内使用虚表调度。
像GCC这样的vc++不会优化调用。使用Visual Studio 2013在发布模式下构建,/O2标志:
base->Func();
010B12D2 mov eax,dword ptr [esi] //load V-Table
010B12D4 mov ecx,esi //load this pointer into ecx
010B12D6 call dword ptr [eax] //call the first function in the V-Table.
编辑:你的问题实际上展示了一些很好的东西。esi
装this
。解引用this
如何得到v表?因为V-Table是多态对象在其内存中拥有的第一个"变量"。因此,汇编方面,*this()
实际上产生对第一个函数的调用,*(this+sizeof(void*))()
调用v表中的第二个函数,以此类推。
这很像将类声明为
class A{
VTABLE vtable;
//rest of the variables.
}
编译器不能优化这个虚函数调用,因为Derived
的虚函数表符号是外部可见的,并且可以在运行时使用共享对象和LD_PRELOAD
环境变量重写。
我认为它应该能够优化调用,如果你使用-fvisibility=hidden
参数
- 是否可以使用函数指针调用虚拟析构函数?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 使用在堆栈上创建的对象调用虚拟函数
- 如何从派生类函数中调用虚拟函数
- 从内部类的析构函数调用虚拟函数
- 使用范围解析运算符时,在构造函数中调用虚拟方法是否安全?
- 调用虚拟成员类的方法
- 为什么不调用虚拟基非默认构造函数,除非大多数派生基显式调用它们?
- 为什么在运行时调用虚拟函数时需要虚拟表
- 从C++中的虚拟析构函数调用虚拟方法
- 如何在C++中调用虚拟析构函数
- 调用虚拟函数而不通过类类型指针创建任何对象
- 通过命名成员调用虚拟与地址或引用的区别
- 从派生的指针中调用虚拟功能,而无需支付VTable价格
- 为什么我不能从铸造的字节(char)数组中调用虚拟函数
- 在基类崩溃中调用虚拟功能
- 可以通过跨二进制边界传递的对象上的接口调用虚拟函数吗?
- 调用虚拟方法的不同方法
- C++未根据需要调用虚拟方法