gpt4 book ai didi

c++ - 为什么我观察到多重继承比单一继承要快?

转载 作者:IT老高 更新时间:2023-10-28 14:01:19 25 4
gpt4 key购买 nike

我有以下两个文件:-

single.cpp :-

#include <iostream>
#include <stdlib.h>

using namespace std;

unsigned long a=0;

class A {
public:
virtual int f() __attribute__ ((noinline)) { return a; }
};

class B : public A {
public:
virtual int f() __attribute__ ((noinline)) { return a; }
void g() __attribute__ ((noinline)) { return; }
};

int main() {
cin>>a;
A* obj;
if (a>3)
obj = new B();
else
obj = new A();

unsigned long result=0;

for (int i=0; i<65535; i++) {
for (int j=0; j<65535; j++) {
result+=obj->f();
}
}

cout<<result<<"\n";
}

还有

multiple.cpp :-

#include <iostream>
#include <stdlib.h>

using namespace std;

unsigned long a=0;

class A {
public:
virtual int f() __attribute__ ((noinline)) { return a; }
};

class dummy {
public:
virtual void g() __attribute__ ((noinline)) { return; }
};

class B : public A, public dummy {
public:
virtual int f() __attribute__ ((noinline)) { return a; }
virtual void g() __attribute__ ((noinline)) { return; }
};


int main() {
cin>>a;
A* obj;
if (a>3)
obj = new B();
else
obj = new A();

unsigned long result=0;

for (int i=0; i<65535; i++) {
for (int j=0; j<65535; j++) {
result+=obj->f();
}
}

cout<<result<<"\n";
}

我正在使用带有标志 -O2 的 gcc 版本 3.4.6

这是我得到的计时结果:-

多个:-

real    0m8.635s
user 0m8.608s
sys 0m0.003s

单人:-

real    0m10.072s
user 0m10.045s
sys 0m0.001s

另一方面,如果在 multiple.cpp 中,我会颠倒类派生的顺序:-

class B : public dummy, public A {

然后我得到以下时间(由于代码需要对 this 指针进行“thunk”调整,这比单继承要慢一些):-

real    0m11.516s
user 0m11.479s
sys 0m0.002s

知道为什么会发生这种情况吗?就循环而言,为所有三种情况生成的程序集似乎没有任何区别。我还有其他地方需要看吗?

此外,我已将进程绑定(bind)到特定的 cpu 内核,并使用 SCHED_RR 以实时优先级运行它。

编辑:- 这被 Mysticial 注意到并由我复制。做一个

cout << "vtable: " << *(void**)obj << endl;

就在 single.cpp 中的循环导致 single 也像 public A、public dummy 一样在 8.4 s 时与多个时钟一样快。

最佳答案

请注意,这个答案是高度推测性的。

与我对“为什么 X 比 Y 慢”类型的问题的其他一些答案不同,我无法提供确凿的证据来支持这个答案。


经过大约一个小时的修改,我认为这是由于三件事的地址对齐:

(owagh's answer 也暗示了指令对齐的可能性。)

多重继承比单继承慢的原因不是因为它“神奇地”快,而是因为单继承情况正在运行到编译器或硬件“打嗝”。


如果您为单继承和多继承情况转储程序集,它们在嵌套循环中是相同的(寄存器名称和所有内容)。

这是我编译的代码:

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
unsigned long a=0;


#ifdef SINGLE
class A {
public:
virtual int f() { return a; }
};

class B : public A {
public:
virtual int f() { return a; }
void g() { return; }
};
#endif

#ifdef MULTIPLE
class A {
public:
virtual int f() { return a; }
};

class dummy {
public:
virtual void g() { return; }
};

class B : public A, public dummy {
public:
virtual int f() { return a; }
virtual void g() { return; }
};
#endif

int main() {
cin >> a;
A* obj;
if (a > 3)
obj = new B();
else
obj = new A();

unsigned long result = 0;


clock_t time0 = clock();

for (int i=0; i<65535; i++) {
for (int j=0; j<65535; j++) {
result += obj->f();
}
}

clock_t time1 = clock();
cout << (double)(time1 - time0) / CLOCKS_PER_SEC << endl;

cout << result << "\n";
system("pause"); // This is useless in Linux, but I left it here for a reason.
}

嵌套循环的程序集在单继承和多继承情况下是相同的:

.L5:
call clock
movl $65535, %r13d
movq %rax, %r14
xorl %r12d, %r12d
.p2align 4,,10
.p2align 3
.L6:
movl $65535, %ebx
.p2align 4,,10
.p2align 3
.L7:
movq 0(%rbp), %rax
movq %rbp, %rdi
call *(%rax)
cltq
addq %rax, %r12
subl $1, %ebx
jne .L7
subl $1, %r13d
jne .L6
call clock

但我看到的性能差异是:

  • 单曲:9.4 秒
  • 多个:8.06 秒

至强 X5482、Ubuntu、GCC 4.6.1 x64。

这使我得出结论,差异必须取决于数据。

如果您查看该程序集,您会注意到唯一可能具有可变延迟的指令是负载:

    ; %rbp = vtable

movq 0(%rbp), %rax ; Dereference function pointer from vtable
movq %rbp, %rdi
call *(%rax) ; Call function pointer - f()

随后在调用 f() 内进行了几次内存访问。


恰好在单继承示例中,上述值的偏移量对处理器不利。我不知道为什么。 但我不得不怀疑一些事情,它会以类似于 region 2 in the diagram of this question 的方式发生缓存库冲突。 .

通过重新排列代码和添加虚拟函数,我可以更改这些偏移量 - 在很多情况下,这将消除这种减速并使单继承与多继承情况一样快。


例如,去掉 system("pause") 会使时间倒转:

#ifdef SINGLE
class A {
public:
virtual int f() { return a; }
};

class B : public A {
public:
virtual int f() { return a; }
void g() { return; }
};
#endif

#ifdef MULTIPLE
class A {
public:
virtual int f() { return a; }
};

class dummy {
public:
virtual void g() { return; }
};

class B : public A, public dummy {
public:
virtual int f() { return a; }
virtual void g() { return; }
};
#endif

int main() {
cin >> a;
A* obj;
if (a > 3)
obj = new B();
else
obj = new A();

unsigned long result = 0;


clock_t time0 = clock();

for (int i=0; i<65535; i++) {
for (int j=0; j<65535; j++) {
result += obj->f();
}
}

clock_t time1 = clock();
cout << (double)(time1 - time0) / CLOCKS_PER_SEC << endl;

cout << result << "\n";
// system("pause");
}
  • 单曲:8.06 秒
  • 多个:9.4 秒

关于c++ - 为什么我观察到多重继承比单一继承要快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10439094/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com