在学校,我们关于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.cpp
,B.cpp
和main.cpp
。另外,我将f()
(A::f()
和B::f()
)函数实际上做某事(x = 0 - x
其中x
是{ {1}} public
成员int
,在A
中初始化为1)。将其编译为六个版本,这是我的最终结果:
A::A()
虚拟时未优化仍然快1秒,我觉得有点奇怪。但这是一个很好的实验,并感谢你们所有人的答案!
答案 0 :(得分:11)
一旦vtable在缓存中,实际执行某些操作的虚拟和非虚函数之间的性能差异非常小。在使用C ++开发软件时,您通常不应该关注自己。正如其他人所指出的那样,在C ++中对未经优化的代码进行基准测试是毫无意义的。
答案 1 :(得分:6)
分析未经优化的代码几乎毫无意义。使用-O2
生成有意义的结果。使用-O3
可能会导致更快的代码,但除非您将A::f
和B::f
分别编译为main
(即,在单独的编译单元中),否则它可能无法生成实际结果
根据反馈,甚至可能-O2
过于激进。 2 ms的结果是因为编译器完全优化了循环。直接电话不 快;事实上,应该很难观察到任何明显的差异。将f
的实现移动到单独的编译单元中以获取实数。在.h中定义类,但在自己的.cc文件中定义A::f
和B::f
。
答案 2 :(得分:2)
考虑到CPU在重新组织你的代码,交错计算和内存访问的过程中做了多少,我不会过多地读到4%的差异 - 这是不值得担心的,因为你无法得出任何合理的结论来自这样的微基准。
尝试真正的计算,通过实际内存访问来了解虚拟方法花费多少钱。虚拟方法本身通常不是问题 - 现代cpu会将vtable中的指针提取与其他工作交错 - 缺少内联会导致性能下降。
答案 3 :(得分:1)
鉴于程序的简单性,编译器很有可能优化某些东西,或沿着那条线的东西。增加复杂性/使编译器完全按照你想要的方式编译是你应该针对这种事情的东西(在运行时差异是我相信只有2个解引用,所以比函数调用的其余部分少)。实现这一点的一种方法是,正如@Marcelo所说,在主文件的单独文件中编译A和B - 我会更进一步,在每个文件中编译它们。但是,我不同意他的意思,由于上述原因,你应该关闭优化,以便编译器生成代码的字面翻译,并且不会删除。