当指向的类型始终相同时,我对虚拟调用的成本有疑问:
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;
}
编译器会优化此虚拟呼叫吗?
答案 0 :(得分:2)
GCC与-O3似乎无法优化虚拟通话。
.L5
movq (%rdx), %rdx
cmpq Derived::Func(), %rdx
je .L3
movq %rbp, %rdi
call *%rdx
subl $1, %ebx
jne .L11
这会进行函数指针比较,如果不相等,则执行间接函数调用。
答案 1 :(得分:2)
很难优化功能的虚拟性。您无法在正确的编译时知道虚拟不会产生任何影响。只有在链接时才能解决这个问题。
更有问题的是你甚至可能无法知道,因为你可以动态加载实现另一个可能覆盖虚函数的子类的共享库。
基本上这种优化需要非常智能的链接时间优化,因为这可能是非常小的增益。
答案 2 :(得分:1)
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-Table?好吧,因为V-Table是第一个&#34;变量&#34;多态对象具有内存。所以汇编方面,*this()
实际上产生了对第一个函数的调用,*(this+sizeof(void*))()
调用了V-Table中的第二个函数,依此类推。
它就像宣布你的班级一样
class A{
VTABLE vtable;
//rest of the variables.
}
答案 3 :(得分:1)
这取决于编译器的智能性;也许它可以优化,也许不是。它也是一个实现细节 - 您更关心代码的整体性能,而不是像这样的具体细节。衡量您的表现并决定是否需要优化,如果需要,您的方法。
如果您真的关心这样的代码,您可以选择使用C ++ 11关键字gsub
,并对循环外的类型进行测试,而不是仅在循环内使用vtable dispatch。
答案 4 :(得分:0)
编译器无法优化此虚拟调用,因为Derived
的vtable符号在外部可见,并且可以在运行时使用共享对象和LD_PRELOAD
环境变量覆盖。
如果您使用-fvisibility=hidden
参数,我认为它应该能够优化通话。