为什么执行时方法解析比编译时解析更快?

时间:2010-05-27 21:33:39

标签: c++ oop virtual

在学校,我们关于C ++中的virtual函数,以及它们如何已解决(或找到匹配,在执行时而不是编译时,我不知道术语是什么 - 我们不是用英语学习。老师还告诉我们,编译时分辨率比执行时间快得多(并且它是有意义的)。但是,快速实验会暗示其他情况。我已经建立了这个小程序:

#include <iostream>
#include <limits.h>

using namespace std;

class A {
    public:
    void f() {
        // do nothing
    }
};

class B: public A {
    public:
    void f() {
        // do nothing
    }
};

int main() {
    unsigned int i;
    A *a = new B;
    for (i=0; i < UINT_MAX; i++) a->f();
    return 0;
}

我编写了上面的程序,并将其命名为normal。然后,我将A修改为如下所示:

class A {
    public:
    virtual void f() {
        // do nothing
    }
};

编译并命名为virtual。以下是我的结果:

[felix@the-machine C]$ time ./normal 

real    0m25.834s
user    0m25.742s
sys 0m0.000s
[felix@the-machine C]$ time ./virtual 

real    0m24.630s
user    0m24.472s
sys 0m0.003s
[felix@the-machine C]$ time ./normal 

real    0m25.860s
user    0m25.735s
sys 0m0.007s
[felix@the-machine C]$ time ./virtual 

real    0m24.514s
user    0m24.475s
sys 0m0.000s
[felix@the-machine C]$ time ./normal 

real    0m26.022s
user    0m25.795s
sys 0m0.013s
[felix@the-machine C]$ time ./virtual 

real    0m24.503s
user    0m24.468s
sys 0m0.000s

似乎有一个稳定的~1秒的差异有利于虚拟版本。这是为什么?


相关与否:双核奔腾@ 2.80Ghz,两次测试之间没有额外的应用程序运行。 Archlinux与gcc 4.5.0。正常编译,如:

$ g++ test.cpp -o normal

此外,-Wall也不会吐出任何警告。


修改:我已将我的计划分为A.cppB.cppmain.cpp。另外,我将f()A::f()B::f())函数实际上某事(x = 0 - x其中x是{ {1}} public成员int,在A中初始化为1)。将其编译为六个版本,这是我的最终结果:

A::A()

虚拟时未优化仍然快1秒,我觉得有点奇怪。但这是一个很好的实验,并感谢你们所有人的答案!

4 个答案:

答案 0 :(得分:11)

一旦vtable在缓存中,实际执行某些操作的虚拟和非虚函数之间的性能差异非常小。在使用C ++开发软件时,您通常不应该关注自己。正如其他人所指出的那样,在C ++中对未经优化的代码进行基准测试是毫无意义的。

答案 1 :(得分:6)

分析未经优化的代码几乎毫无意义。使用-O2生成有意义的结果。使用-O3可能会导致更快的代码,但除非您将A::fB::f分别编译为main(即,在单独的编译单元中),否则它可能无法生成实际结果

根据反馈,甚至可能-O2过于激进。 2 ms的结果是因为编译器完全优化了循环。直接电话不 快;事实上,应该很难观察到任何明显的差异。将f的实现移动到单独的编译单元中以获取实数。在.h中定义类,但在自己的.cc文件中定义A::fB::f

答案 2 :(得分:2)

考虑到CPU在重新组织你的代码,交错计算和内存访问的过程中做了多少,我不会过多地读到4%的差异 - 这是不值得担心的,因为你无法得出任何合理的结论来自这样的微基准。

尝试真正的计算,通过实际内存访问来了解虚拟方法花费多少钱。虚拟方法本身通常不是问题 - 现代cpu会将vtable中的指针提取与其他工作交错 - 缺少内联会导致性能下降。

答案 3 :(得分:1)

鉴于程序的简单性,编译器很有可能优化某些东西,或沿着那条线的东西。增加复杂性/使编译器完全按照你想要的方式编译是你应该针对这种事情的东西(在运行时差异是我相信只有2个解引用,所以比函数调用的其余部分少)。实现这一点的一种方法是,正如@Marcelo所说,在主文件的单独文件中编译A和B - 我会更进一步,在每个文件中编译它们。但是,我不同意他的意思,由于上述原因,你应该关闭优化,以便编译器生成代码的字面翻译,并且不会删除。