これはあなたの実際の質問に対する答えではありませんが、「仮想関数の呼び出しと通常のクラス関数の呼び出しのオーバーヘッドは実際にはどうなるか」について興味がありました。「公平」にするために、非常に単純な関数を実装する classes.cpp を作成しましたが、それは「メイン」の外でコンパイルされる別のファイルです。
クラス.h:
#ifndef CLASSES_H
#define CLASSES_H
class base
{
virtual int vfunc(int x) = 0;
};
class vclass : public base
{
public:
int vfunc(int x);
};
class nvclass
{
public:
int nvfunc(int x);
};
nvclass *nvfactory();
vclass* vfactory();
#endif
クラス.cpp:
#include "classes.h"
int vclass:: vfunc(int x)
{
return x+1;
}
int nvclass::nvfunc(int x)
{
return x+1;
}
nvclass *nvfactory()
{
return new nvclass;
}
vclass* vfactory()
{
return new vclass;
}
これは次から呼び出されます。
#include <cstdio>
#include <cstdlib>
#include "classes.h"
#if 0
#define ASSERT(x) do { if(!(x)) { assert_fail( __FILE__, __LINE__, #x); } } while(0)
static void assert_fail(const char* file, int line, const char *cond)
{
fprintf(stderr, "ASSERT failed at %s:%d condition: %s \n", file, line, cond);
exit(1);
}
#else
#define ASSERT(x) (void)(x)
#endif
#define SIZE 10000000
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
void print_avg(const char *str, const int *diff, int size)
{
int i;
long sum = 0;
for(i = 0; i < size; i++)
{
int t = diff[i];
sum += t;
}
printf("%s average =%f clocks\n", str, (double)sum / size);
}
int diff[SIZE];
int main()
{
unsigned long long a, b;
int i;
int sum = 0;
int x;
vclass *v = vfactory();
nvclass *nv = nvfactory();
for(i = 0; i < SIZE; i++)
{
a = rdtsc();
x = 16;
sum+=x;
b = rdtsc();
diff[i] = (int)(b - a);
}
print_avg("Emtpy", diff, SIZE);
for(i = 0; i < SIZE; i++)
{
a = rdtsc();
x = 0;
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
x = v->vfunc(x);
ASSERT(x == 4);
sum+=x;
b = rdtsc();
diff[i] = (int)(b - a);
}
print_avg("Virtual", diff, SIZE);
for(i = 0; i < SIZE; i++)
{
a = rdtsc();
x = 0;
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
x = nv->nvfunc(x);
ASSERT(x == 4);
sum+=x;
b = rdtsc();
diff[i] = (int)(b - a);
}
print_avg("no virtual", diff, SIZE);
printf("sum=%d\n", sum);
delete v;
delete nv;
return 0;
}
コードの本当の違いはこれです: 仮想呼び出し:
40066b: ff 10 callq *(%rax)
非仮想呼び出し:
4006d3: e8 78 01 00 00 callq 400850 <_ZN7nvclass6nvfuncEi>
そして結果:
Emtpy average =78.686081 clocks
Virtual average =144.732567 clocks
no virtual average =122.781466 clocks
sum=480000000
これはループあたり 16 回の呼び出しのオーバーヘッドであるため、関数を呼び出す場合と関数を呼び出さない場合の違いは、反復ごとに約 5 クロック サイクル (結果の合計と必要なその他の処理を含む) であり、仮想呼び出しでは 1 回あたり 22 クロックが追加されます。繰り返しなので、呼び出しごとに約 1.5 クロックです。
関数で return x + 1 よりも少し意味のあることを行うと仮定すると、気付くとは思えません。