У меня есть следующие два файла: -
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";
}
Я использую gcc версии 3.4.6 с флагами -O2
И это результаты таймингов, которые я получаю: -
несколько: -
real 0m8.635s
user 0m8.608s
sys 0m0.003s
single: -
real 0m10.072s
user 0m10.045s
sys 0m0.001s
С другой стороны, если в multiple.cpp я инвертирую порядок вывода класса таким образом: -
class B : public dummy, public A {
Затем я получаю следующие тайминги (которые немного медленнее, чем для одиночного наследования, как можно было бы ожидать благодаря настройкам "thunk" для этого указателя, который должен выполнить код): -
real 0m11.516s
user 0m11.479s
sys 0m0.002s
Любая идея, почему это может произойти? Кажется, что нет никакой разницы в сборке, сгенерированной для всех трех случаев, в отношении цикла. Есть ли другое место, на которое мне нужно посмотреть?
Кроме того, я привязал процесс к конкретному ядру процессора, и я запускаю его в режиме реального времени с SCHED_RR.
EDIT: - Это было замечено Mystical и воспроизведено мной. Выполнение
cout << "vtable: " << *(void**)obj << endl;
как раз перед тем, как цикл в single.cpp приведет к тому, что он также будет таким же быстрым, как и множественный такт в 8,4 с, как открытый публичный макет A.