代码之家  ›  专栏  ›  技术社区  ›  Felix

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

  •  1
  • Felix  · 技术社区  · 15 年前

    在学校,我们大约 virtual C++中的函数及其用法 断然的 (或) 建立 匹配的 ,我不知道术语是什么——我们不是用英语学习),而是在执行时而不是编译时。老师还告诉我们编译时解析比执行时间快得多(这样做是有道理的)。然而,一个快速的实验会提出另外的建议。我创建了这个小程序:

    #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
        }
    };
    

    编译并命名 事实上的 . 以下是我的结果:

    [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,两次测试之间没有运行额外的应用程序。带GCC 4.5.0的ArchLinux。正常编译,例如:

    $ g++ test.cpp -o normal
    

    也, -Wall 也不会发出任何警告。


    编辑: 我把我的节目分成 A.cpp , B.cpp main.cpp . 另外,我做了 f() (两者) A::f() B::f() )实际功能 某物( x = 0 - x 哪里 x 是一个 public int 成员 ,初始化为1英寸 A::A() )把它编译成六个版本,下面是我的最终结果:

    [felix@the-machine poo]$ time ./normal-unoptimized 
    
    real    0m31.172s
    user    0m30.621s
    sys 0m0.033s
    [felix@the-machine poo]$ time ./normal-O2
    
    real    0m2.417s
    user    0m2.363s
    sys 0m0.007s
    [felix@the-machine poo]$ time ./normal-O3
    
    real    0m2.495s
    user    0m2.447s
    sys 0m0.000s
    [felix@the-machine poo]$ time ./virtual-unoptimized 
    
    real    0m32.386s
    user    0m32.111s
    sys 0m0.010s
    [felix@the-machine poo]$ time ./virtual-O2
    
    real    0m26.875s
    user    0m26.668s
    sys 0m0.003s
    [felix@the-machine poo]$ time ./virtual-O3
    
    real    0m26.905s
    user    0m26.645s
    sys 0m0.017s
    

    当虚拟的时候,未优化的速度还是快1秒,我觉得有点奇怪。但这是一个很好的实验,感谢你们所有人的回答!

    4 回复  |  直到 15 年前
        1
  •  6
  •   Marcelo Cantos    15 年前

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

    基于反馈,甚至 -O2 太激进了。2毫秒的结果是因为编译器完全优化了循环。直拨电话不是 那个 很快;事实上,要观察到任何明显的差异应该是非常困难的。移动的实现 f 到一个单独的编译单元中得到实数。在a.h中定义类,但定义 答:F B:F 在自己的.cc文件中。

        2
  •  11
  •   anon    15 年前

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

        3
  •  2
  •   mdma    15 年前

    考虑到CPU在重新组织代码、交错计算和内存访问的情况下做了多少工作,我不会将太多的内容理解为4%的差异——这是不值得担心的,因为这样的微基准无法得出任何合理的结论。

    尝试一个真实的计算,通过真实的内存访问来了解虚拟方法的成本。虚拟方法本身通常不是问题——现代CPU将把vtable获取的指针与其他工作交织在一起——正是缺少内联会降低性能。

        4
  •  1
  •   Jared Pochtar    15 年前

    考虑到程序的简单性,编译器很有可能正在优化掉某些东西,或者这一行中的某些东西。增加复杂性/使编译器编译完全符合您的需要,这是您应该针对这类事情(在运行时的区别是,我相信只有2个解引用,因此小于函数调用的其余部分)。如@marcelo所说,实现这一点的一种方法是,将A和B分别编译成与主文件不同的文件——我将更进一步,将每个文件编译成自己的文件。但是,我不同意他的观点,因为上面提到的原因,您应该关闭优化,这样编译器就可以生成代码的文本翻译,并且不会删除任何内容。