Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 带优化的虚拟呼叫成本_C++ - Fatal编程技术网

C++ 带优化的虚拟呼叫成本

C++ 带优化的虚拟呼叫成本,c++,C++,当所指类型始终相同时,我有一个关于虚拟通话成本的问题: 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

当所指类型始终相同时,我有一个关于虚拟通话成本的问题:

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;
}
类基
{
Base(){};
虚void Func()=0;
};
类派生
:公共基地
{
派生():基(){};
void Func(){/*做某事*/};
};
int main()
{
Base*Base=新导出的;
对于(int i=0;i<1000;++i)
{
base->Func();
}
返回0;
}

编译器会优化这个虚拟调用吗

带有-O3的GCC似乎无法优化虚拟呼叫


这将进行函数指针比较,如果不相等,则继续执行间接函数调用。

类似VC++的GCC不会优化调用。 使用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保存
。取消引用
如何给出V表?因为V-Table是多态对象内存中的第一个“变量”。因此,在汇编方面,
*this()
实际上会生成对第一个函数的调用,
*(this+sizeof(void*))()
调用V表中的第二个函数,依此类推
这很像是将您的类声明为

class A{
   VTABLE vtable;
   //rest of the variables.
}

这取决于编译器的智能性;也许它可以优化,也许不能。这也是一个实现细节——您更关心代码的整体性能,而不是像这样的具体细节。衡量您的绩效,并决定是否需要优化,如果需要,请确定您的方法


如果你真的关心这样的代码,你可以选择使用C++11关键字
final
,在循环外测试类型,而不是在循环内使用vtable dispatch。

很难优化函数的虚拟性。您无法在适当的编译时知道虚拟机不会有任何影响。只有在链接时间你才能弄清楚

更大的问题是,您甚至可能无法知道,因为您可能会动态加载实现另一个子类的共享库,该子类可能会覆盖虚拟函数


基本上,这种优化需要非常智能的链接时间优化,因为这可能是非常小的增益。

编译器无法优化此虚拟调用,因为导出的
vtable的符号在外部可见,并且可以在运行时使用共享对象和
LD\u PRELOAD
环境变量重写


我认为如果使用
-fvisibility=hidden
参数,它应该能够优化调用。

我更担心调用
new
。这甚至不会编译。请修复它。您必须将派生中的
Func()
声明为虚拟,以便编译器知道它链接到基类中的
Func()
。否则,它将只定义另一个函数,该函数将通过“名称隐藏”在基类中隐藏该函数,在派生对象上调用该函数时,该函数将按预期工作,但在基类对象上调用该函数将调用基类函数。幸运的是,您在这里声明它是纯虚拟的,所以它根本不会编译,因为您从未实现过它。它还需要一个返回类型。@RyanP在派生类中实现虚函数时,不需要使用
virtual
。@Q-bertsuit
derived::Func()
是虚函数,因为它重写了
Base::Func()
。从实现
Func()
Derived
派生的类将实现一个虚拟的
Func()
。您提到的这些障碍是实现细节。不可能不会发生,因为某些实现是以一种不可能的方式构造的
Derived::Func()
是空的,编译器知道,因此如果调用该函数,它将被跳过,
je
直接进入循环的开始,因此必须存在函数指针不指向
Derived::Func()
的情况,否则将不需要处理。这种情况下,符号被覆盖。优化仍然是次优的,因为它可以从循环中拉出,并且整个循环在函数指针等于
Derived::Func()
的情况下进行了优化。因此,您的论点是,因为此特定版本的GCC生成了该代码,所以没有更好的优化了?你猜怎么着?由于已知
base
指向一个
Derived
对象,并且
Derived::base
不起任何作用,因此一致性实现可能只是跳过循环和所有
vtable
填充,然后从main返回(可能需要调用
操作符new
,但仅此而已),我的论点是代码是正确的,因为ELF标准允许重写符号,所以它确实需要在每次循环迭代中解析虚拟函数调用(因为被重写的
Derived::func()
可能会破坏对象并在其位置构造另一个对象),使用函数内联副本的优化取决于虚拟函数调用是否保持不变。使用ELF也是一个实现细节,其他编译器会生成其他类型的可执行文件。我的论点不是代码在任何方面都是错误的(它不是AFAICT)。我的论点是,由于基于实现细节的观察,您不能得出这样的优化是不可能的结论。在这个例子中,我们可以看到,它唯一可以观察到的行为是它退出并可能调用
操作符new
——一个符合性的实现允许只调用
操作符new(1000)
,并为此特定程序终止。
class A{
   VTABLE vtable;
   //rest of the variables.
}