На самом деле это не ответ на ваш реальный вопрос, но мне было любопытно, «что на самом деле является накладными расходами на вызов виртуальной функции по сравнению с вызовом обычной функции класса». Чтобы сделать это «справедливым», я создал class.cpp, который реализует очень простую функцию, но это отдельный файл, который компилируется вне «основного».
классы.ч:
#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 тактов на итерацию [включая суммирование результатов и другую необходимую обработку], а виртуальный вызов добавляет 22 такта на каждую итерацию. итерация, поэтому около 1,5 часов на вызов.
Сомневаюсь, что вы это заметите, если вы сделаете что-то более значимое, чем возврат x + 1 в своей функции.
person
Mats Petersson
schedule
14.01.2013
call
) на фиксированный адрес. В случае виртуальной функции она будет выполнять косвенный вызов через виртуальную таблицу. Многие процессоры имеют специальные инструкции для вычисления адреса (lea
и подобные...). Если ваши функции в основном пусты, стоимость фактического тела функции будет определять общую стоимость операции. - person David Rodríguez - dribeas   schedule 15.01.2013